From a5a6083e9b4e7fa0b278ee6826448dac572c0e3b Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 10 Feb 2025 10:28:03 +0100 Subject: [PATCH 01/26] test: [ANDROAPP-6802] Create CheckValidationRules use case --- .../data/DataSetInstanceRepositoryImpl.kt | 13 ++++ .../aggregates/domain/CheckValidationRules.kt | 31 ++++++++++ .../model/ValidationRulesConfiguration.kt | 7 +++ .../aggregates/ui/DataSetTableScreen.kt | 29 +++++++++ .../ui/viewModel/DataSetTableViewModel.kt | 19 ++++++ .../domain/CheckValidationRulesTest.kt | 59 +++++++++++++++++++ gradle/libs.versions.toml | 3 +- 7 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesConfiguration.kt create mode 100644 aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt diff --git a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt index 8ce275528d..9e063c6bd2 100644 --- a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt +++ b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt @@ -70,6 +70,19 @@ internal class DataSetInstanceRepositoryImpl( .byDataSetUid().eq(dataSetUid) .blockingGet().map(Section::toDataSetSection) + override suspend fun areValidationRulesMandatory(dataSetUid: String): Boolean { + return d2.dataSetModule() + .dataSets().uid(dataSetUid) + .blockingGet()?.validCompleteOnly() ?: false + } + + override suspend fun checkIfHasValidationRules(dataSetUid: String): Boolean { + return !d2.validationModule().validationRules() + .byDataSetUids(listOf(dataSetUid)) + .bySkipFormValidation().isFalse + .blockingIsEmpty() + } + override suspend fun getRenderingConfig( dataSetUid: String, ) = d2.dataSetModule().dataSets() diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt new file mode 100644 index 0000000000..88a68014a5 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt @@ -0,0 +1,31 @@ +package org.dhis2.mobile.aggregates.domain + +import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.MANDATORY +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.NONE +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.OPTIONAL + +internal class CheckValidationRules( + private val dataSetUid: String, + private val dataSetInstanceRepository: DataSetInstanceRepository, +) { + suspend operator fun invoke(): ValidationRulesConfiguration { + val hasValidationRules = dataSetInstanceRepository.checkIfHasValidationRules( + dataSetUid = dataSetUid, + ) + + return if (hasValidationRules) { + val mandatory = dataSetInstanceRepository.areValidationRulesMandatory( + dataSetUid = dataSetUid, + ) + if (mandatory) { + MANDATORY + } else { + OPTIONAL + } + } else { + NONE + } + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesConfiguration.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesConfiguration.kt new file mode 100644 index 0000000000..8e0c1d0a37 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesConfiguration.kt @@ -0,0 +1,7 @@ +package org.dhis2.mobile.aggregates.model + +internal enum class ValidationRulesConfiguration { + NONE, + MANDATORY, + OPTIONAL, +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt index 85e6bc3abb..f8b0db0eb2 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt @@ -5,6 +5,9 @@ package org.dhis2.mobile.aggregates.ui import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy @@ -19,6 +22,10 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Done import androidx.compose.material.icons.filled.Sync +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Done +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -51,6 +58,8 @@ import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.dhis2.mobile.aggregates.ui.viewModel.DataSetTableViewModel import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle +import org.hisp.dhis.mobile.ui.designsystem.component.FAB +import org.hisp.dhis.mobile.ui.designsystem.component.FABStyle import org.hisp.dhis.mobile.ui.designsystem.component.IconButton import org.hisp.dhis.mobile.ui.designsystem.component.IconButtonStyle import org.hisp.dhis.mobile.ui.designsystem.component.InputDialog @@ -152,6 +161,26 @@ fun DataSetInstanceScreen( ), ) }, + floatingActionButton = { + AnimatedVisibility( + visible = true, + enter = fadeIn(), + exit = fadeOut(), + ) { + FAB( + style = FABStyle.SECONDARY, + icon = { + Icon( + imageVector = Icons.Outlined.Done, + contentDescription = "Save Button", + ) + }, + onClick = { + dataSetTableViewModel.onSaveClicked() + }, + ) + } + }, ) { Box( modifier = Modifier.fillMaxSize() diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index 1de0347adb..c4576d1a3a 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -11,12 +11,14 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope +import org.dhis2.mobile.aggregates.domain.CheckValidationRules import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators import org.dhis2.mobile.aggregates.domain.GetDataValueData import org.dhis2.mobile.aggregates.domain.GetDataValueInput import org.dhis2.mobile.aggregates.domain.ResourceManager +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.mapper.toInputData import org.dhis2.mobile.aggregates.model.mapper.toTableModel @@ -38,6 +40,7 @@ internal class DataSetTableViewModel( private val getDataValueInput: GetDataValueInput, private val setDataValue: SetDataValue, private val resourceManager: ResourceManager, + private val checkValidationRules: CheckValidationRules, private val dispatcher: Dispatcher, ) : ViewModel() { @@ -212,4 +215,20 @@ internal class DataSetTableViewModel( } } } + + fun onSaveClicked() { + viewModelScope.launch { + when (checkValidationRules()) { + ValidationRulesConfiguration.NONE -> { + // TODO check if dataset instance is complete + } + ValidationRulesConfiguration.MANDATORY -> { + // TODO run validation rules + } + ValidationRulesConfiguration.OPTIONAL -> { + // TODO show validation rule dialog to ask if user wants to run validation rules + } + } + } + } } diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt new file mode 100644 index 0000000000..26ab7e1bbc --- /dev/null +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt @@ -0,0 +1,59 @@ +package org.dhis2.mobile.aggregates.domain + +import kotlinx.coroutines.test.runTest +import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration +import org.junit.Assert.assertEquals +import org.mockito.Mockito.mock +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.whenever +import kotlin.test.Test + +class CheckValidationRulesTest { + + private val dataSetInstanceRepository: DataSetInstanceRepository = mock() + + @Test + fun `should return NONE when there are no validation rules`() = runTest { + // Given + val dataSetUid = "dataSetUid" + val checkValidationRules = CheckValidationRules(dataSetUid, dataSetInstanceRepository) + whenever(dataSetInstanceRepository.checkIfHasValidationRules(dataSetUid)) doReturn false + + // When + val result = checkValidationRules() + + // Then + assertEquals(ValidationRulesConfiguration.NONE, result) + } + + @Test + fun `should return Mandatory when validation rules are mandatory`() = runTest { + // Given + val dataSetUid = "dataSetUid" + val checkValidationRules = CheckValidationRules(dataSetUid, dataSetInstanceRepository) + whenever(dataSetInstanceRepository.checkIfHasValidationRules(dataSetUid)) doReturn true + whenever(dataSetInstanceRepository.areValidationRulesMandatory(dataSetUid)) doReturn true + + // When + val result = checkValidationRules() + + // Then + assertEquals(ValidationRulesConfiguration.MANDATORY, result) + } + + @Test + fun `should return Optional when validation rules are optional`() = runTest { + // Given + val dataSetUid = "dataSetUid" + val checkValidationRules = CheckValidationRules(dataSetUid, dataSetInstanceRepository) + whenever(dataSetInstanceRepository.checkIfHasValidationRules(dataSetUid)) doReturn true + whenever(dataSetInstanceRepository.areValidationRulesMandatory(dataSetUid)) doReturn false + + // When + val result = checkValidationRules() + + // Then + assertEquals(ValidationRulesConfiguration.OPTIONAL, result) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index db44f12541..124c072d2d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -59,13 +59,12 @@ root = "0.0.7" openid = "0.8.1" conscrypt = "2.5.2" gson = "2.9.0" -#gsonconverter = "2.9.0" okhttp = "4.9.3" jodatime = "2.10.5" glide = "4.9.0" guava = "31.1-android" mockito = "5.2.0" -mockito-kotlin = "5.1.0" +mockito-kotlin = "5.4.0" junit = "4.13.2" mockito_inline = "5.2.0" javafaker = "1.0.2" From 05ed275ce82ed34352bcd0c078724a460bfa5910 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 11 Feb 2025 14:15:25 +0100 Subject: [PATCH 02/26] test: [ANDROAPP-6802] Create CheckCompletionStatus Use Case --- .../data/DataSetInstanceRepositoryImpl.kt | 16 +++++++ .../data/DataSetInstanceRepository.kt | 11 +++++ .../mobile/aggregates/di/AggregateModule.kt | 31 ++++++++++++ .../domain/CheckCompletionStatus.kt | 30 ++++++++++++ .../model/DataSetCompletionStatus.kt | 6 +++ .../ui/viewModel/DataSetTableViewModel.kt | 47 ++++++++++++++++--- 6 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckCompletionStatus.kt create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetCompletionStatus.kt diff --git a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt index 9e063c6bd2..c7e19806a7 100644 --- a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt +++ b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt @@ -70,6 +70,22 @@ internal class DataSetInstanceRepositoryImpl( .byDataSetUid().eq(dataSetUid) .blockingGet().map(Section::toDataSetSection) + override suspend fun isComplete( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attrOptionComboUid: String, + ): Boolean { + return d2.dataSetModule().dataSetCompleteRegistrations() + .byDataSetUid().eq(dataSetUid) + .byPeriod().eq(periodId) + .byOrganisationUnitUid().eq(orgUnitUid) + .byAttributeOptionComboUid().eq(attrOptionComboUid) + .byDeleted().isFalse + .isEmpty() + .map { isEmpty -> !isEmpty }.blockingGet() + } + override suspend fun areValidationRulesMandatory(dataSetUid: String): Boolean { return d2.dataSetModule() .dataSets().uid(dataSetUid) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt index 3ab28e9fbf..7574fbebcb 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt @@ -93,4 +93,15 @@ internal interface DataSetInstanceRepository { suspend fun categoryOptionComboFromCategoryOptions(categoryOptions: List): String suspend fun getCoordinatesFrom(value: String): Pair + + suspend fun checkIfHasValidationRules(dataSetUid: String): Boolean + + suspend fun areValidationRulesMandatory(dataSetUid: String): Boolean + + suspend fun isComplete( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attrOptionComboUid: String, + ): Boolean } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt index b85d2a4c16..edbf5158e4 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt @@ -1,6 +1,8 @@ package org.dhis2.mobile.aggregates.di import kotlinx.coroutines.Dispatchers +import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus +import org.dhis2.mobile.aggregates.domain.CheckValidationRules import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators @@ -85,6 +87,23 @@ internal val featureModule = module { ) } + factory { params -> + CheckValidationRules( + dataSetUid = params.get(), + dataSetInstanceRepository = get(), + ) + } + + factory { params -> + CheckCompletionStatus( + dataSetUid = params.get(), + periodId = params.get(), + orgUnitUid = params.get(), + attrOptionComboUid = params.get(), + dataSetInstanceRepository = get(), + ) + } + viewModel { params -> val dataSetUid = params.get() val periodId = params.get() @@ -116,6 +135,18 @@ internal val featureModule = module { parametersOf(periodId, orgUnitUid, attrOptionComboUid) }, resourceManager = get(), + checkValidationRules = get { + parametersOf(dataSetUid) + }, + checkCompletionStatus = get { + parametersOf(dataSetUid, periodId, orgUnitUid, attrOptionComboUid) + }, + checkMandatoryFieldsStatus = get { + parametersOf(dataSetUid) + }, + datasetModalDialogProvider = get { + parametersOf() + }, dispatcher = get(), ) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckCompletionStatus.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckCompletionStatus.kt new file mode 100644 index 0000000000..a67c181933 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckCompletionStatus.kt @@ -0,0 +1,30 @@ +package org.dhis2.mobile.aggregates.domain + +import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository +import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus +import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.COMPLETED +import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.NOT_COMPLETED + +internal class CheckCompletionStatus( + private val dataSetUid: String, + private val periodId: String, + private val orgUnitUid: String, + private val attrOptionComboUid: String, + private val dataSetInstanceRepository: DataSetInstanceRepository, +) { + + suspend operator fun invoke(): DataSetCompletionStatus { + val isComplete = dataSetInstanceRepository.isComplete( + dataSetUid = dataSetUid, + periodId = periodId, + orgUnitUid = orgUnitUid, + attrOptionComboUid = attrOptionComboUid, + ) + + return if (isComplete) { + COMPLETED + } else { + NOT_COMPLETED + } + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetCompletionStatus.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetCompletionStatus.kt new file mode 100644 index 0000000000..7b98339a69 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetCompletionStatus.kt @@ -0,0 +1,6 @@ +package org.dhis2.mobile.aggregates.model + +internal enum class DataSetCompletionStatus { + COMPLETED, + NOT_COMPLETED, +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index c4576d1a3a..147272b4ad 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope +import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus import org.dhis2.mobile.aggregates.domain.CheckValidationRules import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData @@ -18,7 +19,11 @@ import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators import org.dhis2.mobile.aggregates.domain.GetDataValueData import org.dhis2.mobile.aggregates.domain.GetDataValueInput import org.dhis2.mobile.aggregates.domain.ResourceManager -import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration +import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.COMPLETED +import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.NOT_COMPLETED +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.MANDATORY +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.NONE +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.OPTIONAL import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.mapper.toInputData import org.dhis2.mobile.aggregates.model.mapper.toTableModel @@ -41,6 +46,7 @@ internal class DataSetTableViewModel( private val setDataValue: SetDataValue, private val resourceManager: ResourceManager, private val checkValidationRules: CheckValidationRules, + private val checkCompletionStatus: CheckCompletionStatus, private val dispatcher: Dispatcher, ) : ViewModel() { @@ -217,18 +223,47 @@ internal class DataSetTableViewModel( } fun onSaveClicked() { - viewModelScope.launch { + viewModelScope.launch(dispatcher.io()) { when (checkValidationRules()) { - ValidationRulesConfiguration.NONE -> { - // TODO check if dataset instance is complete + NONE -> { + attemptToFinnish() } - ValidationRulesConfiguration.MANDATORY -> { + + MANDATORY -> { // TODO run validation rules } - ValidationRulesConfiguration.OPTIONAL -> { + + OPTIONAL -> { // TODO show validation rule dialog to ask if user wants to run validation rules } } } } + + private fun attemptToFinnish() { + viewModelScope.launch(dispatcher.io()) { + when (checkCompletionStatus()) { + COMPLETED -> TODO("Save and finish") + NOT_COMPLETED -> { + _dataSetScreenState.update { + if (it is DataSetScreenState.Loaded) { + it.copy(showCompletionDialog = true) + } else { + it + } + } + } + } + } + } + + fun onCompletionDialogDismissed() { + _dataSetScreenState.update { + if (it is DataSetScreenState.Loaded) { + it.copy(showCompletionDialog = false) + } else { + it + } + } + } } From 3675186579e87df3ef94b2c671f1f81a0382adc7 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 11 Feb 2025 23:35:51 +0100 Subject: [PATCH 03/26] test: [ANDROAPP-6802] Create flow when none validation rules --- .../composeResources/values/strings.xml | 5 ++ .../mobile/aggregates/di/AggregateModule.kt | 7 +++ .../aggregates/domain/ResourceManager.kt | 15 +++++ .../aggregates/ui/DataSetTableScreen.kt | 26 ++++++++ .../ui/provider/DatasetModalDialogProvider.kt | 60 +++++++++++++++++++ .../ui/states/DataSetModalDialogUIState.kt | 10 ++++ .../ui/states/DataSetScreenState.kt | 11 ++++ .../ui/viewModel/DataSetTableViewModel.kt | 33 ++++++++-- 8 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt diff --git a/aggregates/src/commonMain/composeResources/values/strings.xml b/aggregates/src/commonMain/composeResources/values/strings.xml index 42887b8a3f..0d470a7aeb 100644 --- a/aggregates/src/commonMain/composeResources/values/strings.xml +++ b/aggregates/src/commonMain/composeResources/values/strings.xml @@ -16,4 +16,9 @@ Out of range Incorrect format Not supported + Everything looks good + Do you also want to complete the dataset? + Not now + Complete + Saved! \ No newline at end of file diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt index edbf5158e4..9a6947956a 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt @@ -11,6 +11,7 @@ import org.dhis2.mobile.aggregates.domain.GetDataValueInput import org.dhis2.mobile.aggregates.domain.ResourceManager import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher +import org.dhis2.mobile.aggregates.ui.provider.DatasetModalDialogProvider import org.dhis2.mobile.aggregates.ui.viewModel.DataSetTableViewModel import org.koin.core.module.Module import org.koin.core.module.dsl.singleOf @@ -104,6 +105,12 @@ internal val featureModule = module { ) } + factory { + DatasetModalDialogProvider( + resourceManager = get(), + ) + } + viewModel { params -> val dataSetUid = params.get() val periodId = params.get() diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt index 8a3beaba50..ae6fdd2831 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt @@ -1,11 +1,26 @@ package org.dhis2.mobile.aggregates.domain import org.dhis2.mobile.aggregates.resources.Res +import org.dhis2.mobile.aggregates.resources.complete +import org.dhis2.mobile.aggregates.resources.completion_dialog_description +import org.dhis2.mobile.aggregates.resources.completion_dialog_title import org.dhis2.mobile.aggregates.resources.default_column_label import org.dhis2.mobile.aggregates.resources.total_header_label +import org.dhis2.mobile.aggregates.resources.not_now +import org.dhis2.mobile.aggregates.resources.saved import org.jetbrains.compose.resources.getString internal class ResourceManager { suspend fun defaultHeaderLabel() = getString(Res.string.default_column_label) suspend fun totalsHeader() = getString(Res.string.total_header_label) + + suspend fun provideCompletionDialogTitle() = getString(Res.string.completion_dialog_title) + + suspend fun provideCompletionDialogDescription() = getString(Res.string.completion_dialog_description) + + suspend fun provideNotNow() = getString(Res.string.not_now) + + suspend fun provideComplete() = getString(Res.string.complete) + + suspend fun provideSaved() = getString(Res.string.saved) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt index f8b0db0eb2..25d211972d 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt @@ -30,9 +30,13 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -58,6 +62,7 @@ import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.dhis2.mobile.aggregates.ui.viewModel.DataSetTableViewModel import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle +import org.hisp.dhis.mobile.ui.designsystem.component.BottomSheetShell import org.hisp.dhis.mobile.ui.designsystem.component.FAB import org.hisp.dhis.mobile.ui.designsystem.component.FABStyle import org.hisp.dhis.mobile.ui.designsystem.component.IconButton @@ -115,6 +120,17 @@ fun DataSetInstanceScreen( } } + val snackbarHostState = remember { SnackbarHostState() } + + LaunchedEffect(dataSetScreenState.snackbarMessage) { + dataSetScreenState.snackbarMessage.collect { message -> + snackbarHostState.showSnackbar( + message = message, + duration = SnackbarDuration.Long, + ) + } + } + Scaffold( modifier = Modifier .fillMaxSize(), @@ -161,6 +177,7 @@ fun DataSetInstanceScreen( ), ) }, + snackbarHost = { SnackbarHost(snackbarHostState) }, floatingActionButton = { AnimatedVisibility( visible = true, @@ -316,6 +333,15 @@ fun DataSetInstanceScreen( } } } + + (dataSetScreenState as? DataSetScreenState.Loaded)?.modalDialog?.let { dataSetUIState -> + BottomSheetShell( + uiState = dataSetUIState.contentDialogUIState, + content = null, + buttonBlock = dataSetUIState.buttonsDialog, + onDismiss = dataSetUIState.onDismiss, + ) + } } /** diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt new file mode 100644 index 0000000000..e9a1bc16f5 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt @@ -0,0 +1,60 @@ +package org.dhis2.mobile.aggregates.ui.provider + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import org.dhis2.mobile.aggregates.domain.ResourceManager +import org.dhis2.mobile.aggregates.ui.states.DataSetModalDialogUIState +import org.hisp.dhis.mobile.ui.designsystem.component.Button +import org.hisp.dhis.mobile.ui.designsystem.component.ButtonBlock +import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle +import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellDefaults +import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellUIState + +internal class DatasetModalDialogProvider( + val resourceManager: ResourceManager, +) { + + suspend fun provideCompletionDialog( + onDismiss: () -> Unit, + onNotNow: () -> Unit, + onComplete: () -> Unit, + ): DataSetModalDialogUIState { + val notNowText = resourceManager.provideNotNow() + val completeText = resourceManager.provideComplete() + + return DataSetModalDialogUIState( + contentDialogUIState = BottomSheetShellUIState( + title = resourceManager.provideCompletionDialogTitle(), + subtitle = resourceManager.provideCompletionDialogDescription(), + showBottomSectionDivider = false, + headerTextAlignment = TextAlign.Start, + ), + buttonsDialog = { + ButtonBlock( + modifier = Modifier.padding( + BottomSheetShellDefaults.buttonBlockPaddings(), + ), + primaryButton = { + Button( + style = ButtonStyle.OUTLINED, + text = notNowText, + onClick = onNotNow, + modifier = Modifier.fillMaxWidth(), + ) + }, + secondaryButton = { + Button( + style = ButtonStyle.FILLED, + text = completeText, + modifier = Modifier.fillMaxWidth(), + onClick = onComplete, + ) + }, + ) + }, + onDismiss = onDismiss, + ) + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt new file mode 100644 index 0000000000..25df5bcc67 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt @@ -0,0 +1,10 @@ +package org.dhis2.mobile.aggregates.ui.states + +import androidx.compose.runtime.Composable +import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellUIState + +internal data class DataSetModalDialogUIState( + val contentDialogUIState: BottomSheetShellUIState, + val buttonsDialog: @Composable (() -> Unit), + val onDismiss: () -> Unit, +) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt index 2076bbf02e..bee6b9d44f 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt @@ -1,17 +1,28 @@ package org.dhis2.mobile.aggregates.ui.states +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow import org.dhis2.mobile.aggregates.model.DataSetDetails import org.dhis2.mobile.aggregates.model.DataSetRenderingConfig import org.dhis2.mobile.aggregates.model.DataSetSection import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableModel internal sealed class DataSetScreenState { + + private val _snackbarMessage = MutableSharedFlow() + val snackbarMessage: SharedFlow get() = _snackbarMessage + + suspend fun sendSnackbarMessage(message: String) { + _snackbarMessage.emit(message) + } + data class Loaded( val dataSetDetails: DataSetDetails, val dataSetSections: List, val renderingConfig: DataSetRenderingConfig, val dataSetSectionTable: DataSetSectionTable, val selectedCellInfo: InputData? = null, + val modalDialog: DataSetModalDialogUIState? = null, ) : DataSetScreenState() { override fun allowTwoPane(canUseTwoPane: Boolean) = dataSetSections.isNotEmpty() && canUseTwoPane && renderingConfig.useVerticalTabs diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index 147272b4ad..0712c161c1 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -33,6 +33,7 @@ import org.dhis2.mobile.aggregates.ui.constants.NO_SECTION_UID import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator import org.dhis2.mobile.aggregates.ui.inputs.UiAction +import org.dhis2.mobile.aggregates.ui.provider.DatasetModalDialogProvider import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableModel @@ -48,6 +49,7 @@ internal class DataSetTableViewModel( private val checkValidationRules: CheckValidationRules, private val checkCompletionStatus: CheckCompletionStatus, private val dispatcher: Dispatcher, + private val datasetModalDialogProvider: DatasetModalDialogProvider, ) : ViewModel() { private val _dataSetScreenState = @@ -242,12 +244,20 @@ internal class DataSetTableViewModel( private fun attemptToFinnish() { viewModelScope.launch(dispatcher.io()) { + val onSavedMessage = resourceManager.provideSaved() + when (checkCompletionStatus()) { - COMPLETED -> TODO("Save and finish") + COMPLETED -> onExit(onSavedMessage) NOT_COMPLETED -> { _dataSetScreenState.update { if (it is DataSetScreenState.Loaded) { - it.copy(showCompletionDialog = true) + it.copy( + modalDialog = datasetModalDialogProvider.provideCompletionDialog( + onDismiss = { onModalDialogDismissed() }, + onNotNow = { onExit(onSavedMessage) }, + onComplete = { attemptToComplete() }, + ), + ) } else { it } @@ -257,13 +267,28 @@ internal class DataSetTableViewModel( } } - fun onCompletionDialogDismissed() { + private fun attemptToComplete() { + TODO("Check Mandatory fields") + } + + private fun onExit(exitMessage: String) { + showSnackbar(exitMessage) + // TODO("Exit") + } + + private fun onModalDialogDismissed() { _dataSetScreenState.update { if (it is DataSetScreenState.Loaded) { - it.copy(showCompletionDialog = false) + it.copy(modalDialog = null) } else { it } } } + + private fun showSnackbar(message: String) { + viewModelScope.launch { + _dataSetScreenState.value.sendSnackbarMessage(message) + } + } } From 78f67beca23f990754a7fe0cb17077d41cc71bbd Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 13 Feb 2025 07:44:48 +0100 Subject: [PATCH 04/26] test: [ANDROAPP-6802] Create CheckMandatoryFieldsStatus use case --- .../composeResources/values/strings.xml | 2 ++ .../mobile/aggregates/di/AggregateModule.kt | 11 ++++-- .../domain/CheckMandatoryFieldsStatus.kt | 11 ++++++ .../aggregates/domain/CheckValidationRules.kt | 10 +++--- .../aggregates/domain/ResourceManager.kt | 6 ++++ .../model/DataSetMandatoryFieldsStatus.kt | 7 ++++ ...=> DataSetValidationRulesConfiguration.kt} | 2 +- .../ui/provider/DatasetModalDialogProvider.kt | 32 +++++++++++++++++ .../ui/viewModel/DataSetTableViewModel.kt | 35 +++++++++++++++---- .../domain/CheckValidationRulesTest.kt | 8 ++--- 10 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckMandatoryFieldsStatus.kt create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetMandatoryFieldsStatus.kt rename aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/{ValidationRulesConfiguration.kt => DataSetValidationRulesConfiguration.kt} (59%) diff --git a/aggregates/src/commonMain/composeResources/values/strings.xml b/aggregates/src/commonMain/composeResources/values/strings.xml index 0d470a7aeb..161cb2ff28 100644 --- a/aggregates/src/commonMain/composeResources/values/strings.xml +++ b/aggregates/src/commonMain/composeResources/values/strings.xml @@ -21,4 +21,6 @@ Not now Complete Saved! + There are missing mandatory fields. Please, fill them to complete the data set. + OK \ No newline at end of file diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt index 9a6947956a..8208693866 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt @@ -3,6 +3,7 @@ package org.dhis2.mobile.aggregates.di import kotlinx.coroutines.Dispatchers import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus import org.dhis2.mobile.aggregates.domain.CheckValidationRules +import org.dhis2.mobile.aggregates.domain.CheckMandatoryFieldsStatus import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators @@ -111,6 +112,12 @@ internal val featureModule = module { ) } + factory { params -> + CheckMandatoryFieldsStatus( + dataSetUid = params.get(), + ) + } + viewModel { params -> val dataSetUid = params.get() val periodId = params.get() @@ -148,12 +155,10 @@ internal val featureModule = module { checkCompletionStatus = get { parametersOf(dataSetUid, periodId, orgUnitUid, attrOptionComboUid) }, + datasetModalDialogProvider = get(), checkMandatoryFieldsStatus = get { parametersOf(dataSetUid) }, - datasetModalDialogProvider = get { - parametersOf() - }, dispatcher = get(), ) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckMandatoryFieldsStatus.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckMandatoryFieldsStatus.kt new file mode 100644 index 0000000000..ae3a350468 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckMandatoryFieldsStatus.kt @@ -0,0 +1,11 @@ +package org.dhis2.mobile.aggregates.domain + +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus + +internal class CheckMandatoryFieldsStatus( + private val dataSetUid: String, +) { + suspend operator fun invoke(): DataSetMandatoryFieldsStatus { + return DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt index 88a68014a5..e676650466 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt @@ -1,16 +1,16 @@ package org.dhis2.mobile.aggregates.domain import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository -import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration -import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.MANDATORY -import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.NONE -import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.OPTIONAL +import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration +import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.MANDATORY +import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.NONE +import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.OPTIONAL internal class CheckValidationRules( private val dataSetUid: String, private val dataSetInstanceRepository: DataSetInstanceRepository, ) { - suspend operator fun invoke(): ValidationRulesConfiguration { + suspend operator fun invoke(): DataSetValidationRulesConfiguration { val hasValidationRules = dataSetInstanceRepository.checkIfHasValidationRules( dataSetUid = dataSetUid, ) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt index ae6fdd2831..e1be1ede54 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt @@ -6,7 +6,9 @@ import org.dhis2.mobile.aggregates.resources.completion_dialog_description import org.dhis2.mobile.aggregates.resources.completion_dialog_title import org.dhis2.mobile.aggregates.resources.default_column_label import org.dhis2.mobile.aggregates.resources.total_header_label +import org.dhis2.mobile.aggregates.resources.mandatory_fields_dialog_description import org.dhis2.mobile.aggregates.resources.not_now +import org.dhis2.mobile.aggregates.resources.ok import org.dhis2.mobile.aggregates.resources.saved import org.jetbrains.compose.resources.getString @@ -23,4 +25,8 @@ internal class ResourceManager { suspend fun provideComplete() = getString(Res.string.complete) suspend fun provideSaved() = getString(Res.string.saved) + + suspend fun provideMandatoryFieldsDialogDescription() = getString(Res.string.mandatory_fields_dialog_description) + + suspend fun provideOK() = getString(Res.string.ok) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetMandatoryFieldsStatus.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetMandatoryFieldsStatus.kt new file mode 100644 index 0000000000..31f153af3b --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetMandatoryFieldsStatus.kt @@ -0,0 +1,7 @@ +package org.dhis2.mobile.aggregates.model + +internal enum class DataSetMandatoryFieldsStatus { + MISSING_MANDATORY_FIELDS, + CHECK_FIELD_COMBINATION, + SUCCESS, +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesConfiguration.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetValidationRulesConfiguration.kt similarity index 59% rename from aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesConfiguration.kt rename to aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetValidationRulesConfiguration.kt index 8e0c1d0a37..ae7c80adaa 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesConfiguration.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetValidationRulesConfiguration.kt @@ -1,6 +1,6 @@ package org.dhis2.mobile.aggregates.model -internal enum class ValidationRulesConfiguration { +internal enum class DataSetValidationRulesConfiguration { NONE, MANDATORY, OPTIONAL, diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt index e9a1bc16f5..87aa943372 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt @@ -57,4 +57,36 @@ internal class DatasetModalDialogProvider( onDismiss = onDismiss, ) } + + suspend fun provideMandatoryFieldsDialog( + onDismiss: () -> Unit, + onAccept: () -> Unit, + ): DataSetModalDialogUIState { + val acceptText = resourceManager.provideOK() + + return DataSetModalDialogUIState( + contentDialogUIState = BottomSheetShellUIState( + title = resourceManager.provideSaved(), + subtitle = resourceManager.provideMandatoryFieldsDialogDescription(), + showBottomSectionDivider = false, + headerTextAlignment = TextAlign.Start, + ), + buttonsDialog = { + ButtonBlock( + modifier = Modifier.padding( + BottomSheetShellDefaults.buttonBlockPaddings(), + ), + primaryButton = { + Button( + style = ButtonStyle.FILLED, + text = acceptText, + modifier = Modifier.fillMaxWidth(), + onClick = onAccept, + ) + }, + ) + }, + onDismiss = onDismiss, + ) + } } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index 0712c161c1..42c91c449b 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus +import org.dhis2.mobile.aggregates.domain.CheckMandatoryFieldsStatus import org.dhis2.mobile.aggregates.domain.CheckValidationRules import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData @@ -21,9 +22,10 @@ import org.dhis2.mobile.aggregates.domain.GetDataValueInput import org.dhis2.mobile.aggregates.domain.ResourceManager import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.COMPLETED import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.NOT_COMPLETED -import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.MANDATORY -import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.NONE -import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.OPTIONAL +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus +import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.MANDATORY +import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.NONE +import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.OPTIONAL import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.mapper.toInputData import org.dhis2.mobile.aggregates.model.mapper.toTableModel @@ -50,6 +52,7 @@ internal class DataSetTableViewModel( private val checkCompletionStatus: CheckCompletionStatus, private val dispatcher: Dispatcher, private val datasetModalDialogProvider: DatasetModalDialogProvider, + private val checkMandatoryFieldsStatus: CheckMandatoryFieldsStatus, ) : ViewModel() { private val _dataSetScreenState = @@ -268,12 +271,32 @@ internal class DataSetTableViewModel( } private fun attemptToComplete() { - TODO("Check Mandatory fields") + viewModelScope.launch(dispatcher.io()) { + when (checkMandatoryFieldsStatus()) { + DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS -> { + _dataSetScreenState.update { + if (it is DataSetScreenState.Loaded) { + it.copy( + modalDialog = datasetModalDialogProvider.provideMandatoryFieldsDialog( + onDismiss = { onModalDialogDismissed() }, + onAccept = { onModalDialogDismissed() }, + ), + ) + } else { + it + } + } + } + + DataSetMandatoryFieldsStatus.CHECK_FIELD_COMBINATION -> TODO() + DataSetMandatoryFieldsStatus.SUCCESS -> TODO() + } + } } private fun onExit(exitMessage: String) { - showSnackbar(exitMessage) - // TODO("Exit") + showSnackbar(exitMessage) // TODO why message is black? + // TODO finish the instance } private fun onModalDialogDismissed() { diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt index 26ab7e1bbc..c574684c3d 100644 --- a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt @@ -2,7 +2,7 @@ package org.dhis2.mobile.aggregates.domain import kotlinx.coroutines.test.runTest import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository -import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration +import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration import org.junit.Assert.assertEquals import org.mockito.Mockito.mock import org.mockito.kotlin.doReturn @@ -24,7 +24,7 @@ class CheckValidationRulesTest { val result = checkValidationRules() // Then - assertEquals(ValidationRulesConfiguration.NONE, result) + assertEquals(DataSetValidationRulesConfiguration.NONE, result) } @Test @@ -39,7 +39,7 @@ class CheckValidationRulesTest { val result = checkValidationRules() // Then - assertEquals(ValidationRulesConfiguration.MANDATORY, result) + assertEquals(DataSetValidationRulesConfiguration.MANDATORY, result) } @Test @@ -54,6 +54,6 @@ class CheckValidationRulesTest { val result = checkValidationRules() // Then - assertEquals(ValidationRulesConfiguration.OPTIONAL, result) + assertEquals(DataSetValidationRulesConfiguration.OPTIONAL, result) } } From 22bde89187b15b8837f2ee0795dcd609d64ddfbe Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 13 Feb 2025 16:19:23 +0100 Subject: [PATCH 05/26] test: [ANDROAPP-6802] check Mandatory fields by field combination --- .../composeResources/values/strings.xml | 2 ++ .../mobile/aggregates/di/AggregateModule.kt | 2 +- .../aggregates/domain/ResourceManager.kt | 8 ++++++- .../ui/provider/DatasetModalDialogProvider.kt | 3 ++- .../ui/viewModel/DataSetTableViewModel.kt | 22 +++++++++++++++++-- 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/aggregates/src/commonMain/composeResources/values/strings.xml b/aggregates/src/commonMain/composeResources/values/strings.xml index 161cb2ff28..e5e4916d74 100644 --- a/aggregates/src/commonMain/composeResources/values/strings.xml +++ b/aggregates/src/commonMain/composeResources/values/strings.xml @@ -22,5 +22,7 @@ Complete Saved! There are missing mandatory fields. Please, fill them to complete the data set. + If you set a value, the whole row needs all of values to complete the Data Set. OK + Saved and completed! \ No newline at end of file diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt index 8208693866..fdd4577071 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt @@ -2,8 +2,8 @@ package org.dhis2.mobile.aggregates.di import kotlinx.coroutines.Dispatchers import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus -import org.dhis2.mobile.aggregates.domain.CheckValidationRules import org.dhis2.mobile.aggregates.domain.CheckMandatoryFieldsStatus +import org.dhis2.mobile.aggregates.domain.CheckValidationRules import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt index e1be1ede54..6461bf4188 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt @@ -6,10 +6,12 @@ import org.dhis2.mobile.aggregates.resources.completion_dialog_description import org.dhis2.mobile.aggregates.resources.completion_dialog_title import org.dhis2.mobile.aggregates.resources.default_column_label import org.dhis2.mobile.aggregates.resources.total_header_label +import org.dhis2.mobile.aggregates.resources.mandatory_fields_combination_message import org.dhis2.mobile.aggregates.resources.mandatory_fields_dialog_description import org.dhis2.mobile.aggregates.resources.not_now import org.dhis2.mobile.aggregates.resources.ok import org.dhis2.mobile.aggregates.resources.saved +import org.dhis2.mobile.aggregates.resources.saved_and_completed import org.jetbrains.compose.resources.getString internal class ResourceManager { @@ -26,7 +28,11 @@ internal class ResourceManager { suspend fun provideSaved() = getString(Res.string.saved) - suspend fun provideMandatoryFieldsDialogDescription() = getString(Res.string.mandatory_fields_dialog_description) + suspend fun provideMandatoryFieldsMessage() = getString(Res.string.mandatory_fields_dialog_description) + + suspend fun provideMandatoryFieldsCombinationMessage() = getString(Res.string.mandatory_fields_combination_message) suspend fun provideOK() = getString(Res.string.ok) + + suspend fun provideSavedAndCompleted() = getString(Res.string.saved_and_completed) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt index 87aa943372..0d8e6be382 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt @@ -59,6 +59,7 @@ internal class DatasetModalDialogProvider( } suspend fun provideMandatoryFieldsDialog( + mandatoryFieldsMessage: String, onDismiss: () -> Unit, onAccept: () -> Unit, ): DataSetModalDialogUIState { @@ -67,7 +68,7 @@ internal class DatasetModalDialogProvider( return DataSetModalDialogUIState( contentDialogUIState = BottomSheetShellUIState( title = resourceManager.provideSaved(), - subtitle = resourceManager.provideMandatoryFieldsDialogDescription(), + subtitle = mandatoryFieldsMessage, showBottomSectionDivider = false, headerTextAlignment = TextAlign.Start, ), diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index 42c91c449b..e2f85f354e 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -278,6 +278,7 @@ internal class DataSetTableViewModel( if (it is DataSetScreenState.Loaded) { it.copy( modalDialog = datasetModalDialogProvider.provideMandatoryFieldsDialog( + mandatoryFieldsMessage = resourceManager.provideMandatoryFieldsMessage(), onDismiss = { onModalDialogDismissed() }, onAccept = { onModalDialogDismissed() }, ), @@ -288,8 +289,25 @@ internal class DataSetTableViewModel( } } - DataSetMandatoryFieldsStatus.CHECK_FIELD_COMBINATION -> TODO() - DataSetMandatoryFieldsStatus.SUCCESS -> TODO() + DataSetMandatoryFieldsStatus.CHECK_FIELD_COMBINATION -> { + _dataSetScreenState.update { + if (it is DataSetScreenState.Loaded) { + it.copy( + modalDialog = datasetModalDialogProvider.provideMandatoryFieldsDialog( + mandatoryFieldsMessage = resourceManager.provideMandatoryFieldsCombinationMessage(), + onDismiss = { onModalDialogDismissed() }, + onAccept = { onModalDialogDismissed() }, + ), + ) + } else { + it + } + } + } + + DataSetMandatoryFieldsStatus.SUCCESS -> { + onExit(resourceManager.provideSavedAndCompleted()) + } } } } From f23167adda4efd597cfb617c9a3b8fa165932aef Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 13 Feb 2025 16:33:59 +0100 Subject: [PATCH 06/26] test: [ANDROAPP-6802] add close callback --- .../kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt | 2 ++ .../org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt | 1 + .../mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt index fdd4577071..7b39b8537a 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt @@ -123,8 +123,10 @@ internal val featureModule = module { val periodId = params.get() val orgUnitUid = params.get() val attrOptionComboUid = params.get() + val onClose = params.get<() -> Unit>() DataSetTableViewModel( + onClose = onClose, getDataSetInstanceData = get { parametersOf( dataSetUid, diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt index 25d211972d..79c28d1468 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt @@ -110,6 +110,7 @@ fun DataSetInstanceScreen( parameters.periodId, parameters.organisationUnitUid, parameters.attributeOptionComboUid, + onBackClicked, ) }) val dataSetScreenState by dataSetTableViewModel.dataSetScreenState.collectAsState() diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index e2f85f354e..f65100ae1e 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -41,6 +41,7 @@ import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableModel internal class DataSetTableViewModel( + private val onClose: () -> Unit, private val getDataSetInstanceData: GetDataSetInstanceData, private val getDataSetSectionData: GetDataSetSectionData, private val getDataValueData: GetDataValueData, @@ -314,7 +315,7 @@ internal class DataSetTableViewModel( private fun onExit(exitMessage: String) { showSnackbar(exitMessage) // TODO why message is black? - // TODO finish the instance + onClose() } private fun onModalDialogDismissed() { From 3b04294229816a98215cbe21d42f8588116dbac9 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 13 Feb 2025 16:40:59 +0100 Subject: [PATCH 07/26] test: [ANDROAPP-6802] move resourceManager to ui --- .../kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt | 2 +- .../mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt | 1 - .../aggregates/{domain => ui/provider}/ResourceManager.kt | 2 +- .../mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt | 2 +- .../mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) rename aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/{domain => ui/provider}/ResourceManager.kt (97%) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt index 7b39b8537a..1e5af98149 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt @@ -9,7 +9,7 @@ import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators import org.dhis2.mobile.aggregates.domain.GetDataValueData import org.dhis2.mobile.aggregates.domain.GetDataValueInput -import org.dhis2.mobile.aggregates.domain.ResourceManager +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher import org.dhis2.mobile.aggregates.ui.provider.DatasetModalDialogProvider diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt index 0d8e6be382..7dbef5679b 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign -import org.dhis2.mobile.aggregates.domain.ResourceManager import org.dhis2.mobile.aggregates.ui.states.DataSetModalDialogUIState import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonBlock diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt similarity index 97% rename from aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt rename to aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt index 6461bf4188..eb2335cb0d 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt @@ -1,4 +1,4 @@ -package org.dhis2.mobile.aggregates.domain +package org.dhis2.mobile.aggregates.ui.provider import org.dhis2.mobile.aggregates.resources.Res import org.dhis2.mobile.aggregates.resources.complete diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index f65100ae1e..4fdf1e4255 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -19,7 +19,7 @@ import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators import org.dhis2.mobile.aggregates.domain.GetDataValueData import org.dhis2.mobile.aggregates.domain.GetDataValueInput -import org.dhis2.mobile.aggregates.domain.ResourceManager +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.COMPLETED import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.NOT_COMPLETED import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt index fad55e91bf..23a7ab69c8 100644 --- a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt @@ -17,7 +17,7 @@ import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators import org.dhis2.mobile.aggregates.domain.GetDataValueData import org.dhis2.mobile.aggregates.domain.GetDataValueInput -import org.dhis2.mobile.aggregates.domain.ResourceManager +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.CellElement import org.dhis2.mobile.aggregates.model.CellInfo From 6f7c122f8904d5283b6c9ab046f19792af354cd8 Mon Sep 17 00:00:00 2001 From: andresmr Date: Fri, 14 Feb 2025 16:00:14 +0100 Subject: [PATCH 08/26] test: [ANDROAPP-6802] CompleteDataSet use case --- .../data/DataSetInstanceRepositoryImpl.kt | 82 +++++++++++++++++++ .../composeResources/values/strings.xml | 1 + .../data/DataSetInstanceRepository.kt | 21 +++++ .../mobile/aggregates/di/AggregateModule.kt | 13 ++- .../domain/CheckMandatoryFieldsStatus.kt | 11 --- .../aggregates/domain/CompleteDataSet.kt | 59 +++++++++++++ .../model/DataSetMandatoryFieldsStatus.kt | 3 +- .../aggregates/ui/provider/ResourceManager.kt | 3 + .../ui/viewModel/DataSetTableViewModel.kt | 23 ++++-- .../ui/viewModel/DataSetTableViewModelTest.kt | 1 + 10 files changed, 193 insertions(+), 24 deletions(-) delete mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckMandatoryFieldsStatus.kt create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSet.kt diff --git a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt index c7e19806a7..63abf95d57 100644 --- a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt +++ b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt @@ -14,12 +14,14 @@ import org.dhis2.mobile.aggregates.model.TableGroup import org.dhis2.mobile.aggregates.ui.constants.NO_SECTION_UID import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.arch.helpers.GeometryHelper +import org.hisp.dhis.android.core.arch.helpers.UidsHelper import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope import org.hisp.dhis.android.core.common.Geometry import org.hisp.dhis.android.core.common.State import org.hisp.dhis.android.core.common.ValueType import org.hisp.dhis.android.core.dataset.DataSetEditableStatus import org.hisp.dhis.android.core.dataset.Section +import org.hisp.dhis.android.core.maintenance.D2Error internal class DataSetInstanceRepositoryImpl( private val d2: D2, @@ -452,4 +454,84 @@ internal class DataSetInstanceRepositoryImpl( ).toString() }.toSortedMap(compareBy { it }) .takeIf { it.isNotEmpty() } + + override suspend fun checkIfHasMissingMandatoryFields( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attributeOptionComboUid: String, + ): Boolean { + return !d2.dataSetModule().dataSets().withCompulsoryDataElementOperands().uid(dataSetUid) + .get() + .map { + it.compulsoryDataElementOperands()?.filter { dataElementOperand -> + dataElementOperand.dataElement()?.let { dataElement -> + dataElementOperand.categoryOptionCombo()?.let { categoryOptionCombo -> + !d2.dataValueModule().dataValues() + .value( + periodId, + orgUnitUid, + dataElement.uid(), + categoryOptionCombo.uid(), + attributeOptionComboUid, + ).blockingExists() + } + } ?: false + } + }.blockingGet().isNullOrEmpty() + } + + override suspend fun checkIfHasMissingMandatoryFieldsCombination( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attributeOptionComboUid: String, + ): Boolean { + return d2.dataSetModule().dataSets().withDataSetElements().uid(dataSetUid).blockingGet() + ?.let { dataSet -> + if (dataSet.fieldCombinationRequired() == true) { + dataSet.dataSetElements() + ?.filter { dataSetElement -> + val catComboUid = dataSetElement.categoryCombo()?.uid() + ?: d2.dataElementModule().dataElements() + .uid(dataSetElement.dataElement().uid()) + .blockingGet()?.categoryComboUid() + val categoryOptionCombos = + d2.categoryModule().categoryOptionCombos().byCategoryComboUid() + .eq(catComboUid).blockingGet() + val dataValueRepository = d2.dataValueModule().dataValues() + .byPeriod().eq(periodId) + .byOrganisationUnitUid().eq(orgUnitUid) + .byAttributeOptionComboUid().eq(attributeOptionComboUid) + .byDeleted().isFalse + .byDataElementUid().eq(dataSetElement.dataElement().uid()) + .byCategoryOptionComboUid() + .`in`(UidsHelper.getUidsList(categoryOptionCombos)) + dataValueRepository.blockingGet().isNotEmpty() && + dataValueRepository + .blockingCount() != categoryOptionCombos.size + }?.map { dataSetElement -> + dataSetElement.dataElement().uid() + }.isNullOrEmpty().not() + } else { + false + } + } ?: false + } + + override suspend fun completeDataset( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attributeOptionComboUid: String, + ): Result { + return try { + d2.dataSetModule().dataSetCompleteRegistrations() + .value(periodId, orgUnitUid, dataSetUid, attributeOptionComboUid) + .blockingSet() + Result.success(Unit) + } catch (error: D2Error) { + Result.failure(error) + } + } } diff --git a/aggregates/src/commonMain/composeResources/values/strings.xml b/aggregates/src/commonMain/composeResources/values/strings.xml index e5e4916d74..bedce29b59 100644 --- a/aggregates/src/commonMain/composeResources/values/strings.xml +++ b/aggregates/src/commonMain/composeResources/values/strings.xml @@ -25,4 +25,5 @@ If you set a value, the whole row needs all of values to complete the Data Set. OK Saved and completed! + There has been an error completing the dataset \ No newline at end of file diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt index 7574fbebcb..20ba5eeff2 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt @@ -104,4 +104,25 @@ internal interface DataSetInstanceRepository { orgUnitUid: String, attrOptionComboUid: String, ): Boolean + + suspend fun checkIfHasMissingMandatoryFields( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attributeOptionComboUid: String, + ): Boolean + + suspend fun checkIfHasMissingMandatoryFieldsCombination( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attributeOptionComboUid: String, + ): Boolean + + suspend fun completeDataset( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attributeOptionComboUid: String, + ): Result } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt index 1e5af98149..01ab5debef 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt @@ -2,8 +2,8 @@ package org.dhis2.mobile.aggregates.di import kotlinx.coroutines.Dispatchers import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus -import org.dhis2.mobile.aggregates.domain.CheckMandatoryFieldsStatus import org.dhis2.mobile.aggregates.domain.CheckValidationRules +import org.dhis2.mobile.aggregates.domain.CompleteDataSet import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators @@ -13,6 +13,7 @@ import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher import org.dhis2.mobile.aggregates.ui.provider.DatasetModalDialogProvider +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.ui.viewModel.DataSetTableViewModel import org.koin.core.module.Module import org.koin.core.module.dsl.singleOf @@ -113,8 +114,12 @@ internal val featureModule = module { } factory { params -> - CheckMandatoryFieldsStatus( + CompleteDataSet( dataSetUid = params.get(), + periodId = params.get(), + orgUnitUid = params.get(), + attrOptionComboUid = params.get(), + dataSetInstanceRepository = get(), ) } @@ -158,8 +163,8 @@ internal val featureModule = module { parametersOf(dataSetUid, periodId, orgUnitUid, attrOptionComboUid) }, datasetModalDialogProvider = get(), - checkMandatoryFieldsStatus = get { - parametersOf(dataSetUid) + completeDataSet = get { + parametersOf(dataSetUid, periodId, orgUnitUid, attrOptionComboUid) }, dispatcher = get(), ) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckMandatoryFieldsStatus.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckMandatoryFieldsStatus.kt deleted file mode 100644 index ae3a350468..0000000000 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckMandatoryFieldsStatus.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.dhis2.mobile.aggregates.domain - -import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus - -internal class CheckMandatoryFieldsStatus( - private val dataSetUid: String, -) { - suspend operator fun invoke(): DataSetMandatoryFieldsStatus { - return DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS - } -} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSet.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSet.kt new file mode 100644 index 0000000000..9bb81b136c --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSet.kt @@ -0,0 +1,59 @@ +package org.dhis2.mobile.aggregates.domain + +import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.ERROR +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS_COMBINATION +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.SUCCESS + +internal class CompleteDataSet( + private val dataSetUid: String, + private val periodId: String, + private val orgUnitUid: String, + private val attrOptionComboUid: String, + private val dataSetInstanceRepository: DataSetInstanceRepository, +) { + suspend operator fun invoke(): DataSetMandatoryFieldsStatus { + return if (checkIfHasMissingMandatoryFields()) { + MISSING_MANDATORY_FIELDS + } else if (checkIfHasMissingMandatoryFieldsCombination()) { + MISSING_MANDATORY_FIELDS_COMBINATION + } else { + dataSetInstanceRepository.completeDataset( + dataSetUid = dataSetUid, + periodId = periodId, + orgUnitUid = orgUnitUid, + attributeOptionComboUid = attrOptionComboUid, + ).fold( + onSuccess = { + SUCCESS + }, + onFailure = { + ERROR + }, + ) + } + } + + private suspend fun checkIfHasMissingMandatoryFields(): Boolean { + val hasMissingMandatoryFields = dataSetInstanceRepository.checkIfHasMissingMandatoryFields( + dataSetUid = dataSetUid, + periodId = periodId, + orgUnitUid = orgUnitUid, + attributeOptionComboUid = attrOptionComboUid, + ) + return hasMissingMandatoryFields + } + + private suspend fun checkIfHasMissingMandatoryFieldsCombination(): Boolean { + val hasMissingMandatoryFieldsCombination = + dataSetInstanceRepository.checkIfHasMissingMandatoryFieldsCombination( + dataSetUid = dataSetUid, + periodId = periodId, + orgUnitUid = orgUnitUid, + attributeOptionComboUid = attrOptionComboUid, + ) + return hasMissingMandatoryFieldsCombination + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetMandatoryFieldsStatus.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetMandatoryFieldsStatus.kt index 31f153af3b..2a5511c728 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetMandatoryFieldsStatus.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetMandatoryFieldsStatus.kt @@ -2,6 +2,7 @@ package org.dhis2.mobile.aggregates.model internal enum class DataSetMandatoryFieldsStatus { MISSING_MANDATORY_FIELDS, - CHECK_FIELD_COMBINATION, + MISSING_MANDATORY_FIELDS_COMBINATION, SUCCESS, + ERROR, } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt index eb2335cb0d..2d1fa88f02 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt @@ -6,6 +6,7 @@ import org.dhis2.mobile.aggregates.resources.completion_dialog_description import org.dhis2.mobile.aggregates.resources.completion_dialog_title import org.dhis2.mobile.aggregates.resources.default_column_label import org.dhis2.mobile.aggregates.resources.total_header_label +import org.dhis2.mobile.aggregates.resources.error_on_complete_dataset import org.dhis2.mobile.aggregates.resources.mandatory_fields_combination_message import org.dhis2.mobile.aggregates.resources.mandatory_fields_dialog_description import org.dhis2.mobile.aggregates.resources.not_now @@ -35,4 +36,6 @@ internal class ResourceManager { suspend fun provideOK() = getString(Res.string.ok) suspend fun provideSavedAndCompleted() = getString(Res.string.saved_and_completed) + + suspend fun provideErrorOnCompleteDataset() = getString(Res.string.error_on_complete_dataset) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index 4fdf1e4255..d889b7c859 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -12,8 +12,8 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus -import org.dhis2.mobile.aggregates.domain.CheckMandatoryFieldsStatus import org.dhis2.mobile.aggregates.domain.CheckValidationRules +import org.dhis2.mobile.aggregates.domain.CompleteDataSet import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators @@ -22,7 +22,10 @@ import org.dhis2.mobile.aggregates.domain.GetDataValueInput import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.COMPLETED import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.NOT_COMPLETED -import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.ERROR +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS_COMBINATION +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.SUCCESS import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.MANDATORY import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.NONE import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.OPTIONAL @@ -36,6 +39,7 @@ import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator import org.dhis2.mobile.aggregates.ui.inputs.UiAction import org.dhis2.mobile.aggregates.ui.provider.DatasetModalDialogProvider +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableModel @@ -53,7 +57,7 @@ internal class DataSetTableViewModel( private val checkCompletionStatus: CheckCompletionStatus, private val dispatcher: Dispatcher, private val datasetModalDialogProvider: DatasetModalDialogProvider, - private val checkMandatoryFieldsStatus: CheckMandatoryFieldsStatus, + private val completeDataSet: CompleteDataSet, ) : ViewModel() { private val _dataSetScreenState = @@ -273,8 +277,8 @@ internal class DataSetTableViewModel( private fun attemptToComplete() { viewModelScope.launch(dispatcher.io()) { - when (checkMandatoryFieldsStatus()) { - DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS -> { + when (completeDataSet()) { + MISSING_MANDATORY_FIELDS -> { _dataSetScreenState.update { if (it is DataSetScreenState.Loaded) { it.copy( @@ -290,7 +294,7 @@ internal class DataSetTableViewModel( } } - DataSetMandatoryFieldsStatus.CHECK_FIELD_COMBINATION -> { + MISSING_MANDATORY_FIELDS_COMBINATION -> { _dataSetScreenState.update { if (it is DataSetScreenState.Loaded) { it.copy( @@ -306,15 +310,18 @@ internal class DataSetTableViewModel( } } - DataSetMandatoryFieldsStatus.SUCCESS -> { + SUCCESS -> { onExit(resourceManager.provideSavedAndCompleted()) } + ERROR -> { + showSnackbar(resourceManager.provideErrorOnCompleteDataset()) + } } } } private fun onExit(exitMessage: String) { - showSnackbar(exitMessage) // TODO why message is black? + showSnackbar(exitMessage) onClose() } diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt index 23a7ab69c8..164801314f 100644 --- a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt @@ -31,6 +31,7 @@ import org.dhis2.mobile.aggregates.model.DataSetSection import org.dhis2.mobile.aggregates.model.InputType import org.dhis2.mobile.aggregates.model.TableGroup import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator import org.dhis2.mobile.aggregates.ui.inputs.TableId import org.dhis2.mobile.aggregates.ui.inputs.TableIdType From 3aa8071b1faa100371db5bc16214ea2d09cd52b9 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 18 Feb 2025 10:03:27 +0100 Subject: [PATCH 09/26] test: [ANDROAPP-6802] Create RunValidationRules use case --- .../data/DataSetInstanceRepositoryImpl.kt | 124 ++++++++++++++++++ .../data/DataSetInstanceRepository.kt | 8 ++ .../mobile/aggregates/di/AggregateModule.kt | 20 ++- ...t => CheckValidationRulesConfiguration.kt} | 12 +- .../aggregates/domain/RunValidationRules.kt | 21 +++ ...ion.kt => ValidationRulesConfiguration.kt} | 2 +- .../aggregates/model/ValidationRulesResult.kt | 38 ++++++ .../ui/viewModel/DataSetTableViewModel.kt | 33 ++++- ... CheckValidationRulesConfigurationTest.kt} | 22 ++-- 9 files changed, 252 insertions(+), 28 deletions(-) rename aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/{CheckValidationRules.kt => CheckValidationRulesConfiguration.kt} (61%) create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/RunValidationRules.kt rename aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/{DataSetValidationRulesConfiguration.kt => ValidationRulesConfiguration.kt} (59%) create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesResult.kt rename aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/{CheckValidationRulesTest.kt => CheckValidationRulesConfigurationTest.kt} (62%) diff --git a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt index 63abf95d57..43b5f8ec51 100644 --- a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt +++ b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepositoryImpl.kt @@ -9,8 +9,12 @@ import org.dhis2.mobile.aggregates.model.DataSetDetails import org.dhis2.mobile.aggregates.model.DataSetInstanceConfiguration import org.dhis2.mobile.aggregates.model.DataSetInstanceSectionConfiguration import org.dhis2.mobile.aggregates.model.DataSetRenderingConfig +import org.dhis2.mobile.aggregates.model.DataToReview import org.dhis2.mobile.aggregates.model.MandatoryCellElements import org.dhis2.mobile.aggregates.model.TableGroup +import org.dhis2.mobile.aggregates.model.ValidationResultStatus +import org.dhis2.mobile.aggregates.model.ValidationRulesResult +import org.dhis2.mobile.aggregates.model.Violation import org.dhis2.mobile.aggregates.ui.constants.NO_SECTION_UID import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.arch.helpers.GeometryHelper @@ -19,9 +23,11 @@ import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope import org.hisp.dhis.android.core.common.Geometry import org.hisp.dhis.android.core.common.State import org.hisp.dhis.android.core.common.ValueType +import org.hisp.dhis.android.core.dataelement.DataElementOperand import org.hisp.dhis.android.core.dataset.DataSetEditableStatus import org.hisp.dhis.android.core.dataset.Section import org.hisp.dhis.android.core.maintenance.D2Error +import org.hisp.dhis.android.core.validation.engine.ValidationResultViolation internal class DataSetInstanceRepositoryImpl( private val d2: D2, @@ -534,4 +540,122 @@ internal class DataSetInstanceRepositoryImpl( Result.failure(error) } } + + override suspend fun runValidationRules( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attrOptionComboUid: String, + ): ValidationRulesResult { + val result = d2.validationModule() + .validationEngine().validate( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ).blockingGet() + + return ValidationRulesResult( + ValidationResultStatus.valueOf(result.status().name), + mapViolations( + violations = result.violations(), + periodId = periodId, + orgUnitUid = orgUnitUid, + attrOptionComboUid = attrOptionComboUid, + ), + ) + } + + private fun mapViolations( + violations: List, + periodId: String, + orgUnitUid: String, + attrOptionComboUid: String, + ): List { + return violations.map { + Violation( + it.validationRule().description(), + it.validationRule().instruction(), + mapDataElements( + dataElementUids = it.dataElementUids(), + periodId = periodId, + orgUnitUid = orgUnitUid, + attrOptionComboUid = attrOptionComboUid, + ), + ) + } + } + + private fun mapDataElements( + dataElementUids: MutableSet, + periodId: String, + orgUnitUid: String, + attrOptionComboUid: String, + ): List { + val dataToReview = arrayListOf() + dataElementUids.mapNotNull { deOperand -> + d2.dataElementModule().dataElements() + .uid(deOperand.dataElement()?.uid()) + .blockingGet()?.let { + Pair(deOperand, it) + } + }.forEach { (deOperand, de) -> + val catOptCombos = + if (deOperand.categoryOptionCombo() != null) { + d2.categoryModule().categoryOptionCombos() + .byUid().like(deOperand.categoryOptionCombo()?.uid()) + .blockingGet() + } else { + d2.categoryModule().categoryOptionCombos() + .byCategoryComboUid().like(de.categoryComboUid()) + .blockingGet() + } + catOptCombos.forEach { catOptCombo -> + val value = if (d2.dataValueModule().dataValues() + .value( + periodId, + orgUnitUid, + de.uid(), + catOptCombo.uid(), + attrOptionComboUid, + ) + .blockingExists() && + d2.dataValueModule().dataValues() + .value( + periodId, + orgUnitUid, + de.uid(), + catOptCombo.uid(), + attrOptionComboUid, + ) + .blockingGet()?.deleted() != true + ) { + d2.dataValueModule().dataValues() + .value( + periodId, + orgUnitUid, + de.uid(), + catOptCombo.uid(), + attrOptionComboUid, + ) + .blockingGet()?.value() ?: "-" + } else { + "-" + } + val isFromDefaultCatCombo = d2.categoryModule().categoryCombos() + .uid(catOptCombo.categoryCombo()?.uid()).blockingGet()?.isDefault == true + dataToReview.add( + DataToReview( + de.uid(), + de.displayFormName(), + catOptCombo.uid(), + catOptCombo.displayName(), + value, + isFromDefaultCatCombo, + ), + ) + } + } + return dataToReview + } } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt index 20ba5eeff2..f8f21a11ae 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/data/DataSetInstanceRepository.kt @@ -8,6 +8,7 @@ import org.dhis2.mobile.aggregates.model.DataSetInstanceSectionConfiguration import org.dhis2.mobile.aggregates.model.DataSetRenderingConfig import org.dhis2.mobile.aggregates.model.DataSetSection import org.dhis2.mobile.aggregates.model.TableGroup +import org.dhis2.mobile.aggregates.model.ValidationRulesResult import java.util.SortedMap internal interface DataSetInstanceRepository { @@ -125,4 +126,11 @@ internal interface DataSetInstanceRepository { orgUnitUid: String, attributeOptionComboUid: String, ): Result + + suspend fun runValidationRules( + dataSetUid: String, + periodId: String, + orgUnitUid: String, + attrOptionComboUid: String, + ): ValidationRulesResult } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt index 01ab5debef..7256a89035 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt @@ -2,7 +2,7 @@ package org.dhis2.mobile.aggregates.di import kotlinx.coroutines.Dispatchers import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus -import org.dhis2.mobile.aggregates.domain.CheckValidationRules +import org.dhis2.mobile.aggregates.domain.CheckValidationRulesConfiguration import org.dhis2.mobile.aggregates.domain.CompleteDataSet import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData @@ -11,6 +11,7 @@ import org.dhis2.mobile.aggregates.domain.GetDataValueData import org.dhis2.mobile.aggregates.domain.GetDataValueInput import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.domain.SetDataValue +import org.dhis2.mobile.aggregates.domain.RunValidationRules import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher import org.dhis2.mobile.aggregates.ui.provider.DatasetModalDialogProvider import org.dhis2.mobile.aggregates.ui.provider.ResourceManager @@ -91,7 +92,7 @@ internal val featureModule = module { } factory { params -> - CheckValidationRules( + CheckValidationRulesConfiguration( dataSetUid = params.get(), dataSetInstanceRepository = get(), ) @@ -123,6 +124,16 @@ internal val featureModule = module { ) } + factory { params -> + RunValidationRules( + dataSetUid = params.get(), + periodId = params.get(), + orgUnitUid = params.get(), + attrOptionComboUid = params.get(), + dataSetInstanceRepository = get(), + ) + } + viewModel { params -> val dataSetUid = params.get() val periodId = params.get() @@ -156,7 +167,7 @@ internal val featureModule = module { parametersOf(periodId, orgUnitUid, attrOptionComboUid) }, resourceManager = get(), - checkValidationRules = get { + checkValidationRulesConfiguration = get { parametersOf(dataSetUid) }, checkCompletionStatus = get { @@ -167,6 +178,9 @@ internal val featureModule = module { parametersOf(dataSetUid, periodId, orgUnitUid, attrOptionComboUid) }, dispatcher = get(), + runValidationRules = get { + parametersOf(dataSetUid, periodId, orgUnitUid, attrOptionComboUid) + }, ) } } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesConfiguration.kt similarity index 61% rename from aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt rename to aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesConfiguration.kt index e676650466..7f1ac638f0 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRules.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesConfiguration.kt @@ -1,16 +1,16 @@ package org.dhis2.mobile.aggregates.domain import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository -import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration -import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.MANDATORY -import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.NONE -import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.OPTIONAL +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.MANDATORY +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.NONE +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.OPTIONAL -internal class CheckValidationRules( +internal class CheckValidationRulesConfiguration( private val dataSetUid: String, private val dataSetInstanceRepository: DataSetInstanceRepository, ) { - suspend operator fun invoke(): DataSetValidationRulesConfiguration { + suspend operator fun invoke(): ValidationRulesConfiguration { val hasValidationRules = dataSetInstanceRepository.checkIfHasValidationRules( dataSetUid = dataSetUid, ) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/RunValidationRules.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/RunValidationRules.kt new file mode 100644 index 0000000000..e2eebd9938 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/domain/RunValidationRules.kt @@ -0,0 +1,21 @@ +package org.dhis2.mobile.aggregates.domain + +import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository +import org.dhis2.mobile.aggregates.model.ValidationRulesResult + +internal class RunValidationRules( + private val dataSetUid: String, + private val periodId: String, + private val orgUnitUid: String, + private val attrOptionComboUid: String, + private val dataSetInstanceRepository: DataSetInstanceRepository, +) { + suspend operator fun invoke(): ValidationRulesResult { + return dataSetInstanceRepository.runValidationRules( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ) + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetValidationRulesConfiguration.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesConfiguration.kt similarity index 59% rename from aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetValidationRulesConfiguration.kt rename to aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesConfiguration.kt index ae7c80adaa..8e0c1d0a37 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/DataSetValidationRulesConfiguration.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesConfiguration.kt @@ -1,6 +1,6 @@ package org.dhis2.mobile.aggregates.model -internal enum class DataSetValidationRulesConfiguration { +internal enum class ValidationRulesConfiguration { NONE, MANDATORY, OPTIONAL, diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesResult.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesResult.kt new file mode 100644 index 0000000000..eb440e347e --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesResult.kt @@ -0,0 +1,38 @@ +package org.dhis2.mobile.aggregates.model + +internal data class ValidationRulesResult( + val validationResultStatus: ValidationResultStatus, + val violations: List, +) + +internal data class Violation( + val description: String?, + val instruction: String?, + val dataToReview: List, +) + +internal data class DataToReview( + val dataElementUid: String, + val dataElementDisplayName: String?, + val categoryOptionComboUid: String, + val categoryOptionComboDisplayName: String?, + val value: String, + val isFromDefaultCatCombo: Boolean, +) { + fun formattedDataLabel(): String { + return if (isFromDefaultCatCombo) { + dataElementDisplayName ?: dataElementUid + } else { + String.format( + "%s | %s", + dataElementDisplayName, + categoryOptionComboDisplayName, + ) + } + } +} + +internal enum class ValidationResultStatus { + OK, + ERROR, +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index d889b7c859..81b0623cd9 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus -import org.dhis2.mobile.aggregates.domain.CheckValidationRules +import org.dhis2.mobile.aggregates.domain.CheckValidationRulesConfiguration import org.dhis2.mobile.aggregates.domain.CompleteDataSet import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData @@ -20,15 +20,17 @@ import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators import org.dhis2.mobile.aggregates.domain.GetDataValueData import org.dhis2.mobile.aggregates.domain.GetDataValueInput import org.dhis2.mobile.aggregates.ui.provider.ResourceManager +import org.dhis2.mobile.aggregates.domain.RunValidationRules import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.COMPLETED import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.NOT_COMPLETED import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.ERROR import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS_COMBINATION import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.SUCCESS -import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.MANDATORY -import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.NONE -import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration.OPTIONAL +import org.dhis2.mobile.aggregates.model.ValidationResultStatus +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.MANDATORY +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.NONE +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.OPTIONAL import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.mapper.toInputData import org.dhis2.mobile.aggregates.model.mapper.toTableModel @@ -53,11 +55,12 @@ internal class DataSetTableViewModel( private val getDataValueInput: GetDataValueInput, private val setDataValue: SetDataValue, private val resourceManager: ResourceManager, - private val checkValidationRules: CheckValidationRules, + private val checkValidationRulesConfiguration: CheckValidationRulesConfiguration, private val checkCompletionStatus: CheckCompletionStatus, private val dispatcher: Dispatcher, private val datasetModalDialogProvider: DatasetModalDialogProvider, private val completeDataSet: CompleteDataSet, + private val runValidationRules: RunValidationRules, ) : ViewModel() { private val _dataSetScreenState = @@ -234,13 +237,13 @@ internal class DataSetTableViewModel( fun onSaveClicked() { viewModelScope.launch(dispatcher.io()) { - when (checkValidationRules()) { + when (checkValidationRulesConfiguration()) { NONE -> { attemptToFinnish() } MANDATORY -> { - // TODO run validation rules + checkValidationRules() } OPTIONAL -> { @@ -250,6 +253,21 @@ internal class DataSetTableViewModel( } } + private fun checkValidationRules() { + viewModelScope.launch(dispatcher.io()) { + val rules = runValidationRules() + when (rules.validationResultStatus) { + ValidationResultStatus.OK -> { + attemptToFinnish() + } + + ValidationResultStatus.ERROR -> { + // TODO show violations dialog + } + } + } + } + private fun attemptToFinnish() { viewModelScope.launch(dispatcher.io()) { val onSavedMessage = resourceManager.provideSaved() @@ -313,6 +331,7 @@ internal class DataSetTableViewModel( SUCCESS -> { onExit(resourceManager.provideSavedAndCompleted()) } + ERROR -> { showSnackbar(resourceManager.provideErrorOnCompleteDataset()) } diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesConfigurationTest.kt similarity index 62% rename from aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt rename to aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesConfigurationTest.kt index c574684c3d..3693db9014 100644 --- a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesTest.kt +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesConfigurationTest.kt @@ -2,14 +2,14 @@ package org.dhis2.mobile.aggregates.domain import kotlinx.coroutines.test.runTest import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository -import org.dhis2.mobile.aggregates.model.DataSetValidationRulesConfiguration +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration import org.junit.Assert.assertEquals import org.mockito.Mockito.mock import org.mockito.kotlin.doReturn import org.mockito.kotlin.whenever import kotlin.test.Test -class CheckValidationRulesTest { +class CheckValidationRulesConfigurationTest { private val dataSetInstanceRepository: DataSetInstanceRepository = mock() @@ -17,43 +17,43 @@ class CheckValidationRulesTest { fun `should return NONE when there are no validation rules`() = runTest { // Given val dataSetUid = "dataSetUid" - val checkValidationRules = CheckValidationRules(dataSetUid, dataSetInstanceRepository) + val checkValidationRulesConfiguration = CheckValidationRulesConfiguration(dataSetUid, dataSetInstanceRepository) whenever(dataSetInstanceRepository.checkIfHasValidationRules(dataSetUid)) doReturn false // When - val result = checkValidationRules() + val result = checkValidationRulesConfiguration() // Then - assertEquals(DataSetValidationRulesConfiguration.NONE, result) + assertEquals(ValidationRulesConfiguration.NONE, result) } @Test fun `should return Mandatory when validation rules are mandatory`() = runTest { // Given val dataSetUid = "dataSetUid" - val checkValidationRules = CheckValidationRules(dataSetUid, dataSetInstanceRepository) + val checkValidationRulesConfiguration = CheckValidationRulesConfiguration(dataSetUid, dataSetInstanceRepository) whenever(dataSetInstanceRepository.checkIfHasValidationRules(dataSetUid)) doReturn true whenever(dataSetInstanceRepository.areValidationRulesMandatory(dataSetUid)) doReturn true // When - val result = checkValidationRules() + val result = checkValidationRulesConfiguration() // Then - assertEquals(DataSetValidationRulesConfiguration.MANDATORY, result) + assertEquals(ValidationRulesConfiguration.MANDATORY, result) } @Test fun `should return Optional when validation rules are optional`() = runTest { // Given val dataSetUid = "dataSetUid" - val checkValidationRules = CheckValidationRules(dataSetUid, dataSetInstanceRepository) + val checkValidationRulesConfiguration = CheckValidationRulesConfiguration(dataSetUid, dataSetInstanceRepository) whenever(dataSetInstanceRepository.checkIfHasValidationRules(dataSetUid)) doReturn true whenever(dataSetInstanceRepository.areValidationRulesMandatory(dataSetUid)) doReturn false // When - val result = checkValidationRules() + val result = checkValidationRulesConfiguration() // Then - assertEquals(DataSetValidationRulesConfiguration.OPTIONAL, result) + assertEquals(ValidationRulesConfiguration.OPTIONAL, result) } } From c4bd7e4a84752e750212cfc0cccd563f8dad46e5 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 18 Feb 2025 11:39:34 +0100 Subject: [PATCH 10/26] test: [ANDROAPP-6802] Adding completion translations --- .../composeResources/values-ar/strings.xml | 8 ++++++++ .../composeResources/values-cs/strings.xml | 7 +++++++ .../composeResources/values-es/strings.xml | 8 ++++++++ .../composeResources/values-fr/strings.xml | 7 +++++++ .../composeResources/values-id/strings.xml | 7 +++++++ .../composeResources/values-lo/strings.xml | 8 ++++++++ .../composeResources/values-nb/strings.xml | 7 +++++++ .../composeResources/values-nl/strings.xml | 8 ++++++++ .../composeResources/values-pt/strings.xml | 8 ++++++++ .../composeResources/values-ru/strings.xml | 5 +++++ .../composeResources/values-uk/strings.xml | 8 ++++++++ .../composeResources/values-uz/strings.xml | 7 +++++++ .../composeResources/values-vi/strings.xml | 8 ++++++++ .../composeResources/values-zh-rCN/strings.xml | 7 +++++++ .../composeResources/values-zh/strings.xml | 8 ++++++++ .../composeResources/values/strings.xml | 6 ++++++ .../dhis2/mobile/aggregates/di/AggregateModule.kt | 4 ++-- ...gProvider.kt => DataSetModalDialogProvider.kt} | 2 +- .../aggregates/ui/provider/ResourceManager.kt | 14 ++++++++------ .../ui/viewModel/DataSetTableViewModel.kt | 4 ++-- .../ui/viewModel/DataSetTableViewModelTest.kt | 15 +++++++++++++++ 21 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 aggregates/src/commonMain/composeResources/values-ar/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-cs/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-es/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-fr/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-id/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-lo/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-nb/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-nl/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-pt/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-ru/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-uk/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-uz/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-vi/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-zh/strings.xml rename aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/{DatasetModalDialogProvider.kt => DataSetModalDialogProvider.kt} (98%) diff --git a/aggregates/src/commonMain/composeResources/values-ar/strings.xml b/aggregates/src/commonMain/composeResources/values-ar/strings.xml new file mode 100644 index 0000000000..78f746db39 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-ar/strings.xml @@ -0,0 +1,8 @@ + + + كل شيء يبدو جيدا! + هل تريد أيضاً تعيين حزمة البيانات كمكتملة؟ + مكتمل + + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-cs/strings.xml b/aggregates/src/commonMain/composeResources/values-cs/strings.xml new file mode 100644 index 0000000000..6a4876cd43 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-cs/strings.xml @@ -0,0 +1,7 @@ + + + Všechno vypadá dobře! + Chcete také vyplnit soubor dat? + Dokončit + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-es/strings.xml b/aggregates/src/commonMain/composeResources/values-es/strings.xml new file mode 100644 index 0000000000..ace56ee92c --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-es/strings.xml @@ -0,0 +1,8 @@ + + + ¡Parece que todo está bien! + ¿Quiere completar también el set de datos? + Ahora no + Completar + + diff --git a/aggregates/src/commonMain/composeResources/values-fr/strings.xml b/aggregates/src/commonMain/composeResources/values-fr/strings.xml new file mode 100644 index 0000000000..1c35da684c --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-fr/strings.xml @@ -0,0 +1,7 @@ + + + Tout a l\'air bien! + Voulez-vous également compléter l\'ensemble de données? + Terminer + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-id/strings.xml b/aggregates/src/commonMain/composeResources/values-id/strings.xml new file mode 100644 index 0000000000..e819c45b48 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-id/strings.xml @@ -0,0 +1,7 @@ + + + Semuanya terlihat bagus! + Apakah Anda juga ingin melengkapi data set? + Lengkap + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-lo/strings.xml b/aggregates/src/commonMain/composeResources/values-lo/strings.xml new file mode 100644 index 0000000000..1b76cc6c82 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-lo/strings.xml @@ -0,0 +1,8 @@ + + + ທຸກຢ່າງຮຽບຮ້ອຍດີ + ທ່ານຕ້ອງການເຮັດຊຸດຂໍ້ມູນໃຫ້ຄົບຖ້ວນບໍ? + ບໍ່​ແມ່ນ​ຕອນ​ນີ້ + ສຳເລັດແລ້ວ + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nb/strings.xml b/aggregates/src/commonMain/composeResources/values-nb/strings.xml new file mode 100644 index 0000000000..db090c36a4 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-nb/strings.xml @@ -0,0 +1,7 @@ + + + Alt ser bra ut + Vil du også fullføre datasettet? + Fullfør + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nl/strings.xml b/aggregates/src/commonMain/composeResources/values-nl/strings.xml new file mode 100644 index 0000000000..63df096336 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-nl/strings.xml @@ -0,0 +1,8 @@ + + + Alles ziet er goed uit! + Wil je ook de dataset compleet maken? + Niet nu + Compleet + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-pt/strings.xml b/aggregates/src/commonMain/composeResources/values-pt/strings.xml new file mode 100644 index 0000000000..2c28070c34 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-pt/strings.xml @@ -0,0 +1,8 @@ + + + Tudo parece bem! + Você também deseja completar o conjunto de dados? + Não agora + Completo + + diff --git a/aggregates/src/commonMain/composeResources/values-ru/strings.xml b/aggregates/src/commonMain/composeResources/values-ru/strings.xml new file mode 100644 index 0000000000..73bc5139b2 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-ru/strings.xml @@ -0,0 +1,5 @@ + + + Завершенный + + diff --git a/aggregates/src/commonMain/composeResources/values-uk/strings.xml b/aggregates/src/commonMain/composeResources/values-uk/strings.xml new file mode 100644 index 0000000000..f87987d10b --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-uk/strings.xml @@ -0,0 +1,8 @@ + + + Все виглядає добре! + Ви бажаєте також завершити набір даних? + Не зараз + Завершити + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-uz/strings.xml b/aggregates/src/commonMain/composeResources/values-uz/strings.xml new file mode 100644 index 0000000000..d0b6c71c19 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-uz/strings.xml @@ -0,0 +1,7 @@ + + + Кўринишидан ҳаммаси яхши! + Маълумотлар тўпламини тўлдиришни хохлайсизми? + Бажарилди + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-vi/strings.xml b/aggregates/src/commonMain/composeResources/values-vi/strings.xml new file mode 100644 index 0000000000..35d49ec8eb --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-vi/strings.xml @@ -0,0 +1,8 @@ + + + Mọi thứ đều ổn! + Bạn cũng muốn hoàn tất biểu nhập phải không? + Không phải bây giờ + Hoàn tất + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml b/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..1a46320eea --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml @@ -0,0 +1,7 @@ + + + 一切都很好 + 你要完成数据集? + 完成 + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh/strings.xml b/aggregates/src/commonMain/composeResources/values-zh/strings.xml new file mode 100644 index 0000000000..f62c69c554 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-zh/strings.xml @@ -0,0 +1,8 @@ + + + 一切看起来都不错! + 你要完成数据集? + 现在不要 + 完成 + + diff --git a/aggregates/src/commonMain/composeResources/values/strings.xml b/aggregates/src/commonMain/composeResources/values/strings.xml index bedce29b59..73dade36db 100644 --- a/aggregates/src/commonMain/composeResources/values/strings.xml +++ b/aggregates/src/commonMain/composeResources/values/strings.xml @@ -18,12 +18,18 @@ Not supported Everything looks good Do you also want to complete the dataset? + Everything looks good + "Do you also want to complete the data set?" Not now Complete + Saved! There are missing mandatory fields. Please, fill them to complete the data set. If you set a value, the whole row needs all of values to complete the Data Set. OK Saved and completed! There has been an error completing the dataset + + Do you also want to complete the dataset? + \ No newline at end of file diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt index 7256a89035..c4ff810839 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt @@ -13,7 +13,7 @@ import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.domain.RunValidationRules import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher -import org.dhis2.mobile.aggregates.ui.provider.DatasetModalDialogProvider +import org.dhis2.mobile.aggregates.ui.provider.DataSetModalDialogProvider import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.ui.viewModel.DataSetTableViewModel import org.koin.core.module.Module @@ -109,7 +109,7 @@ internal val featureModule = module { } factory { - DatasetModalDialogProvider( + DataSetModalDialogProvider( resourceManager = get(), ) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt similarity index 98% rename from aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt rename to aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt index 7dbef5679b..3ff748b3c0 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DatasetModalDialogProvider.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt @@ -11,7 +11,7 @@ import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellDefaults import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellUIState -internal class DatasetModalDialogProvider( +internal class DataSetModalDialogProvider( val resourceManager: ResourceManager, ) { diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt index 2d1fa88f02..03480d0e5a 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt @@ -2,26 +2,26 @@ package org.dhis2.mobile.aggregates.ui.provider import org.dhis2.mobile.aggregates.resources.Res import org.dhis2.mobile.aggregates.resources.complete -import org.dhis2.mobile.aggregates.resources.completion_dialog_description -import org.dhis2.mobile.aggregates.resources.completion_dialog_title import org.dhis2.mobile.aggregates.resources.default_column_label import org.dhis2.mobile.aggregates.resources.total_header_label import org.dhis2.mobile.aggregates.resources.error_on_complete_dataset import org.dhis2.mobile.aggregates.resources.mandatory_fields_combination_message import org.dhis2.mobile.aggregates.resources.mandatory_fields_dialog_description +import org.dhis2.mobile.aggregates.resources.mark_dataset_complete import org.dhis2.mobile.aggregates.resources.not_now import org.dhis2.mobile.aggregates.resources.ok import org.dhis2.mobile.aggregates.resources.saved import org.dhis2.mobile.aggregates.resources.saved_and_completed +import org.dhis2.mobile.aggregates.resources.validation_success_title import org.jetbrains.compose.resources.getString internal class ResourceManager { suspend fun defaultHeaderLabel() = getString(Res.string.default_column_label) suspend fun totalsHeader() = getString(Res.string.total_header_label) - suspend fun provideCompletionDialogTitle() = getString(Res.string.completion_dialog_title) + suspend fun provideCompletionDialogTitle() = getString(Res.string.validation_success_title) - suspend fun provideCompletionDialogDescription() = getString(Res.string.completion_dialog_description) + suspend fun provideCompletionDialogDescription() = getString(Res.string.mark_dataset_complete) suspend fun provideNotNow() = getString(Res.string.not_now) @@ -29,9 +29,11 @@ internal class ResourceManager { suspend fun provideSaved() = getString(Res.string.saved) - suspend fun provideMandatoryFieldsMessage() = getString(Res.string.mandatory_fields_dialog_description) + suspend fun provideMandatoryFieldsMessage() = + getString(Res.string.mandatory_fields_dialog_description) - suspend fun provideMandatoryFieldsCombinationMessage() = getString(Res.string.mandatory_fields_combination_message) + suspend fun provideMandatoryFieldsCombinationMessage() = + getString(Res.string.mandatory_fields_combination_message) suspend fun provideOK() = getString(Res.string.ok) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index 81b0623cd9..5e52575113 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -40,7 +40,7 @@ import org.dhis2.mobile.aggregates.ui.constants.NO_SECTION_UID import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator import org.dhis2.mobile.aggregates.ui.inputs.UiAction -import org.dhis2.mobile.aggregates.ui.provider.DatasetModalDialogProvider +import org.dhis2.mobile.aggregates.ui.provider.DataSetModalDialogProvider import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable @@ -58,7 +58,7 @@ internal class DataSetTableViewModel( private val checkValidationRulesConfiguration: CheckValidationRulesConfiguration, private val checkCompletionStatus: CheckCompletionStatus, private val dispatcher: Dispatcher, - private val datasetModalDialogProvider: DatasetModalDialogProvider, + private val datasetModalDialogProvider: DataSetModalDialogProvider, private val completeDataSet: CompleteDataSet, private val runValidationRules: RunValidationRules, ) : ViewModel() { diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt index 164801314f..4246e1a6dd 100644 --- a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt @@ -12,6 +12,9 @@ import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository import org.dhis2.mobile.aggregates.di.aggregatesModule +import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus +import org.dhis2.mobile.aggregates.domain.CheckValidationRulesConfiguration +import org.dhis2.mobile.aggregates.domain.CompleteDataSet import org.dhis2.mobile.aggregates.domain.GetDataSetInstanceData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators @@ -21,6 +24,7 @@ import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.CellElement import org.dhis2.mobile.aggregates.model.CellInfo +import org.dhis2.mobile.aggregates.domain.RunValidationRules import org.dhis2.mobile.aggregates.model.DataSetDetails import org.dhis2.mobile.aggregates.model.DataSetInstanceConfiguration import org.dhis2.mobile.aggregates.model.DataSetInstanceData @@ -31,6 +35,7 @@ import org.dhis2.mobile.aggregates.model.DataSetSection import org.dhis2.mobile.aggregates.model.InputType import org.dhis2.mobile.aggregates.model.TableGroup import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher +import org.dhis2.mobile.aggregates.ui.provider.DataSetModalDialogProvider import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator import org.dhis2.mobile.aggregates.ui.inputs.TableId @@ -67,6 +72,11 @@ internal class DataSetTableViewModelTest : KoinTest { private lateinit var testDispatcher: TestDispatcher private lateinit var viewModel: DataSetTableViewModel + private lateinit var checkValidationRulesConfiguration: CheckValidationRulesConfiguration + private lateinit var checkCompletionStatus: CheckCompletionStatus + private lateinit var dataSetModalDialogProvider: DataSetModalDialogProvider + private lateinit var completeDataSet: CompleteDataSet + private lateinit var runValidationRules: RunValidationRules @Before fun setUp() = runTest { @@ -91,6 +101,11 @@ internal class DataSetTableViewModelTest : KoinTest { whenever(runBlocking { totalsHeader() }) doReturn "TotalsHeader" } dispatcher = declareMock() + checkValidationRulesConfiguration = declareMock() + checkCompletionStatus = declareMock() + dataSetModalDialogProvider = declareMock() + completeDataSet = declareMock() + runValidationRules = declareMock() whenever(dispatcher.io).thenReturn { testDispatcher } whenever(getDataSetInstanceData(any())).thenReturn( From 1bd3dcac677535d35ba74ed3a541baa4cf92a87c Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 18 Feb 2025 12:49:09 +0100 Subject: [PATCH 11/26] test: [ANDROAPP-6802] Add transifex config --- .tx/config | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.tx/config b/.tx/config index 0fa80f633d..d8ae3194cb 100644 --- a/.tx/config +++ b/.tx/config @@ -64,3 +64,10 @@ source_file = tracker/src/main/res/values/strings.xml source_lang = en type = ANDROID minimum_perc = 0 + +[o:hisp-uio:p:dhis2-android-capture-app:r:aggregates-strings-xml] +file_filter = aggregates/src/commonMain/composeResources/values-/strings.xml +source_file = aggregates/src/commonMain/composeResources/values/strings.xml +source_lang = en +type = ANDROID +minimum_perc = 0 From a6eb8026446923d9346d5bb8c6f3d18b63f005aa Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 19 Feb 2025 10:39:54 +0100 Subject: [PATCH 12/26] test: [ANDROAPP-6802] Add translations --- .../composeResources/values-ar/strings.xml | 6 +++++- .../composeResources/values-ckb/strings.xml | 6 ++++++ .../composeResources/values-cs/strings.xml | 5 +++++ .../composeResources/values-es/strings.xml | 5 +++++ .../composeResources/values-fr/strings.xml | 5 +++++ .../composeResources/values-id/strings.xml | 5 +++++ .../composeResources/values-lo/strings.xml | 4 ++++ .../composeResources/values-nb/strings.xml | 5 +++++ .../composeResources/values-nl/strings.xml | 5 +++++ .../composeResources/values-prs/strings.xml | 6 ++++++ .../composeResources/values-ps/strings.xml | 6 ++++++ .../composeResources/values-pt/strings.xml | 5 +++++ .../composeResources/values-ru/strings.xml | 1 + .../composeResources/values-sv/strings.xml | 6 ++++++ .../composeResources/values-uk/strings.xml | 5 +++++ .../composeResources/values-ur/strings.xml | 6 ++++++ .../composeResources/values-uz/strings.xml | 5 +++++ .../composeResources/values-vi/strings.xml | 5 +++++ .../composeResources/values-zh-rCN/strings.xml | 5 +++++ .../composeResources/values-zh/strings.xml | 5 +++++ .../commonMain/composeResources/values/strings.xml | 10 +++------- .../mobile/aggregates/ui/provider/ResourceManager.kt | 12 ++++++------ 22 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 aggregates/src/commonMain/composeResources/values-ckb/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-prs/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-ps/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-sv/strings.xml create mode 100644 aggregates/src/commonMain/composeResources/values-ur/strings.xml diff --git a/aggregates/src/commonMain/composeResources/values-ar/strings.xml b/aggregates/src/commonMain/composeResources/values-ar/strings.xml index 78f746db39..901ab117be 100644 --- a/aggregates/src/commonMain/composeResources/values-ar/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-ar/strings.xml @@ -3,6 +3,10 @@ كل شيء يبدو جيدا! هل تريد أيضاً تعيين حزمة البيانات كمكتملة؟ مكتمل - + تم الحفظ! + هناك حقول إلزامية مفقودة. من فضلك قم بملؤها لإكمال حزمة البيانات. + إذا قمت بتعيين قيمة ، فإن الصف بأكمله يحتاج إلى كل القيم لإكمال مجموعة البيانات. + موافق + محفوظ ومكتمل \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-ckb/strings.xml b/aggregates/src/commonMain/composeResources/values-ckb/strings.xml new file mode 100644 index 0000000000..65ee42d4a3 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-ckb/strings.xml @@ -0,0 +1,6 @@ + + + تةواو + + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-cs/strings.xml b/aggregates/src/commonMain/composeResources/values-cs/strings.xml index 6a4876cd43..abf1c8f303 100644 --- a/aggregates/src/commonMain/composeResources/values-cs/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-cs/strings.xml @@ -3,5 +3,10 @@ Všechno vypadá dobře! Chcete také vyplnit soubor dat? Dokončit + Uloženo! + Chybí povinná pole. Chcete-li vyplnit soubor dat, vyplňte je prosím. + Pokud nastavíte hodnotu, celý řádek potřebuje k dokončení datové sady všechny hodnoty. + OK + Uloženo a dokončeno! \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-es/strings.xml b/aggregates/src/commonMain/composeResources/values-es/strings.xml index ace56ee92c..e61c311f22 100644 --- a/aggregates/src/commonMain/composeResources/values-es/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-es/strings.xml @@ -4,5 +4,10 @@ ¿Quiere completar también el set de datos? Ahora no Completar + ¡Guardado! + Hay campos obligatorios vacíos. Por favor, rellénelos para poder completar el set de datos. + Si introduce un valor, tendrá que completar toda la fila para poder marcar como Completado el Data Set. + Correcto + ¡Guardado y completado! diff --git a/aggregates/src/commonMain/composeResources/values-fr/strings.xml b/aggregates/src/commonMain/composeResources/values-fr/strings.xml index 1c35da684c..2eb81f93bb 100644 --- a/aggregates/src/commonMain/composeResources/values-fr/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-fr/strings.xml @@ -3,5 +3,10 @@ Tout a l\'air bien! Voulez-vous également compléter l\'ensemble de données? Terminer + Enregistré! + Il manque des champs obligatoires. Veuillez les remplir pour compléter l\'ensemble de données. + Si vous saisissez une valeur, la ligne entière doit être remplie pour compléter l\'ensemble de données. + Ok + Enregistré et terminé! \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-id/strings.xml b/aggregates/src/commonMain/composeResources/values-id/strings.xml index e819c45b48..454e97f975 100644 --- a/aggregates/src/commonMain/composeResources/values-id/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-id/strings.xml @@ -3,5 +3,10 @@ Semuanya terlihat bagus! Apakah Anda juga ingin melengkapi data set? Lengkap + Tersimpan! + Terdapat hal penting yang hilang. Harap mengisinya untuk melengkapi data.set + Jika Anda mengisi, semua baris harus terisi untuk menyelesaikan data set + Ok + Tersimpan dan selesai! \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-lo/strings.xml b/aggregates/src/commonMain/composeResources/values-lo/strings.xml index 1b76cc6c82..312418f244 100644 --- a/aggregates/src/commonMain/composeResources/values-lo/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-lo/strings.xml @@ -4,5 +4,9 @@ ທ່ານຕ້ອງການເຮັດຊຸດຂໍ້ມູນໃຫ້ຄົບຖ້ວນບໍ? ບໍ່​ແມ່ນ​ຕອນ​ນີ້ ສຳເລັດແລ້ວ + ບັນທຶກແລ້ວ! + ຖ້າທ່ານຕັ້ງຄ່າ, ແຖວທັ້ງໝົດຕ້ອງໃຊ້ຄ່າທັ້ງໝົດເພື່ອເຮັດໃຫ້ຊຸດຂໍ້ມູນສົມບູນ + ​ຕົກ​ລົງ + ບັນ​ທຶກ​ແລະ​ສໍາ​ເລັດ​! \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nb/strings.xml b/aggregates/src/commonMain/composeResources/values-nb/strings.xml index db090c36a4..8630caeda0 100644 --- a/aggregates/src/commonMain/composeResources/values-nb/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-nb/strings.xml @@ -3,5 +3,10 @@ Alt ser bra ut Vil du også fullføre datasettet? Fullfør + Lagret! + Det mangler obligatoriske felt. Vennligst fyll dem for å fullføre datasettet. + Hvis du setter en verdi, så trenger hele raden alle verdier for å fullføre datasettet. + OK + Lagret og fullført! \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nl/strings.xml b/aggregates/src/commonMain/composeResources/values-nl/strings.xml index 63df096336..4c7542eb21 100644 --- a/aggregates/src/commonMain/composeResources/values-nl/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-nl/strings.xml @@ -4,5 +4,10 @@ Wil je ook de dataset compleet maken? Niet nu Compleet + Opgeslagen! + Er ontbreken verplichte velden. Gelieve deze in te vullen om de dataset compleet te maken. + Als u een waarde instelt, heeft de hele rij alle waarden nodig om de gegevensset te voltooien. + OK + Opgeslagen en voltooid! \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-prs/strings.xml b/aggregates/src/commonMain/composeResources/values-prs/strings.xml new file mode 100644 index 0000000000..e95f4903ef --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-prs/strings.xml @@ -0,0 +1,6 @@ + + + تأیید + + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-ps/strings.xml b/aggregates/src/commonMain/composeResources/values-ps/strings.xml new file mode 100644 index 0000000000..91b01cc2e3 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-ps/strings.xml @@ -0,0 +1,6 @@ + + + سم دی + + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-pt/strings.xml b/aggregates/src/commonMain/composeResources/values-pt/strings.xml index 2c28070c34..af8099df73 100644 --- a/aggregates/src/commonMain/composeResources/values-pt/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-pt/strings.xml @@ -4,5 +4,10 @@ Você também deseja completar o conjunto de dados? Não agora Completo + Gravado! + Estão faltando campos obrigatórios. Por favor, preencha-os para completar o conjunto de dados. + Se você definir um valor, a linha inteira precisa de todos os valores para concluir o conjunto de dados. + Aprovado + Salvo e concluído! diff --git a/aggregates/src/commonMain/composeResources/values-ru/strings.xml b/aggregates/src/commonMain/composeResources/values-ru/strings.xml index 73bc5139b2..531b3729ed 100644 --- a/aggregates/src/commonMain/composeResources/values-ru/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-ru/strings.xml @@ -1,5 +1,6 @@ Завершенный + ОК diff --git a/aggregates/src/commonMain/composeResources/values-sv/strings.xml b/aggregates/src/commonMain/composeResources/values-sv/strings.xml new file mode 100644 index 0000000000..070b741719 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-sv/strings.xml @@ -0,0 +1,6 @@ + + + ok + + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-uk/strings.xml b/aggregates/src/commonMain/composeResources/values-uk/strings.xml index f87987d10b..e0db0c759b 100644 --- a/aggregates/src/commonMain/composeResources/values-uk/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-uk/strings.xml @@ -4,5 +4,10 @@ Ви бажаєте також завершити набір даних? Не зараз Завершити + Збережено! + Обов\'язкові поля не заповнені. Будь ласка, заповніть їх, щоб завершити набір даних. + Якщо Ви встановлюєте значення, то щодо усього рядка потрібно вказати всі значення, щоб завершити набір даних. + ОК + Збережено та завершено! \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-ur/strings.xml b/aggregates/src/commonMain/composeResources/values-ur/strings.xml new file mode 100644 index 0000000000..039ab35bb2 --- /dev/null +++ b/aggregates/src/commonMain/composeResources/values-ur/strings.xml @@ -0,0 +1,6 @@ + + + ٹھیک + + + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-uz/strings.xml b/aggregates/src/commonMain/composeResources/values-uz/strings.xml index d0b6c71c19..5f391251e7 100644 --- a/aggregates/src/commonMain/composeResources/values-uz/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-uz/strings.xml @@ -3,5 +3,10 @@ Кўринишидан ҳаммаси яхши! Маълумотлар тўпламини тўлдиришни хохлайсизми? Бажарилди + Сақланди! + Шарт бўлган маълумотлар етишмаяпти. Маълумотлар тўпламини шакллантириш учун уларни тўлдиринг. + Агар сиз қийматни ўрнатган бўлсангиз, маълумотлар тўпламини тўлдириш учун қатордаги барча қийматлар бўлиши лозим. + ОК + Якунланди ва сақланди! \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-vi/strings.xml b/aggregates/src/commonMain/composeResources/values-vi/strings.xml index 35d49ec8eb..ae1eb89fe7 100644 --- a/aggregates/src/commonMain/composeResources/values-vi/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-vi/strings.xml @@ -4,5 +4,10 @@ Bạn cũng muốn hoàn tất biểu nhập phải không? Không phải bây giờ Hoàn tất + Đã lưu! + Các trường nhập bắt buộc đang bị thiếu. Xin vui lòng điền giá trị bắt buộc để hoàn thành biểu nhập. + Nếu bạn nhập một giá trị, cần nhập tất cả giá trị để hoàn tất Biểu Nhập + OK + Đã lưu và Hoàn tất! \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml b/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml index 1a46320eea..2d34d3d9d9 100644 --- a/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml @@ -3,5 +3,10 @@ 一切都很好 你要完成数据集? 完成 + 已经保存! + 有强制字段请填充后完成数据集 + 如果你设置值,整行的值需要设置才能完成数据集。 + + 保存并完成! \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh/strings.xml b/aggregates/src/commonMain/composeResources/values-zh/strings.xml index f62c69c554..9ac0893f9f 100644 --- a/aggregates/src/commonMain/composeResources/values-zh/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-zh/strings.xml @@ -4,5 +4,10 @@ 你要完成数据集? 现在不要 完成 + 已经保存! + 有强制字段请填充后完成数据集 + 如果你设置值,整行的值需要设置才能完成数据集。 + + 保存并完成! diff --git a/aggregates/src/commonMain/composeResources/values/strings.xml b/aggregates/src/commonMain/composeResources/values/strings.xml index 73dade36db..0899915614 100644 --- a/aggregates/src/commonMain/composeResources/values/strings.xml +++ b/aggregates/src/commonMain/composeResources/values/strings.xml @@ -22,14 +22,10 @@ "Do you also want to complete the data set?" Not now Complete - Saved! - There are missing mandatory fields. Please, fill them to complete the data set. - If you set a value, the whole row needs all of values to complete the Data Set. + There are missing mandatory fields. Please, fill them to complete the data set. + If you set a value, the whole row needs all of values to complete the Data Set. OK - Saved and completed! + Saved and completed! There has been an error completing the dataset - - Do you also want to complete the dataset? - \ No newline at end of file diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt index 03480d0e5a..f1b3b54dc8 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt @@ -2,16 +2,16 @@ package org.dhis2.mobile.aggregates.ui.provider import org.dhis2.mobile.aggregates.resources.Res import org.dhis2.mobile.aggregates.resources.complete +import org.dhis2.mobile.aggregates.resources.dataset_saved_completed import org.dhis2.mobile.aggregates.resources.default_column_label import org.dhis2.mobile.aggregates.resources.total_header_label import org.dhis2.mobile.aggregates.resources.error_on_complete_dataset -import org.dhis2.mobile.aggregates.resources.mandatory_fields_combination_message -import org.dhis2.mobile.aggregates.resources.mandatory_fields_dialog_description +import org.dhis2.mobile.aggregates.resources.field_mandatory +import org.dhis2.mobile.aggregates.resources.field_required import org.dhis2.mobile.aggregates.resources.mark_dataset_complete import org.dhis2.mobile.aggregates.resources.not_now import org.dhis2.mobile.aggregates.resources.ok import org.dhis2.mobile.aggregates.resources.saved -import org.dhis2.mobile.aggregates.resources.saved_and_completed import org.dhis2.mobile.aggregates.resources.validation_success_title import org.jetbrains.compose.resources.getString @@ -30,14 +30,14 @@ internal class ResourceManager { suspend fun provideSaved() = getString(Res.string.saved) suspend fun provideMandatoryFieldsMessage() = - getString(Res.string.mandatory_fields_dialog_description) + getString(Res.string.field_mandatory) suspend fun provideMandatoryFieldsCombinationMessage() = - getString(Res.string.mandatory_fields_combination_message) + getString(Res.string.field_required) suspend fun provideOK() = getString(Res.string.ok) - suspend fun provideSavedAndCompleted() = getString(Res.string.saved_and_completed) + suspend fun provideSavedAndCompleted() = getString(Res.string.dataset_saved_completed) suspend fun provideErrorOnCompleteDataset() = getString(Res.string.error_on_complete_dataset) } From a037c0b9e10243ad0171de27a8e2bb87a6582d4b Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 19 Feb 2025 12:33:55 +0100 Subject: [PATCH 13/26] test: [ANDROAPP-6802] remove mockito-kotlin update --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 124c072d2d..5c675207e4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -64,7 +64,7 @@ jodatime = "2.10.5" glide = "4.9.0" guava = "31.1-android" mockito = "5.2.0" -mockito-kotlin = "5.4.0" +mockito-kotlin = "5.1.0" junit = "4.13.2" mockito_inline = "5.2.0" javafaker = "1.0.2" From 1a12eb4983a9a1fa55fff5b5d6c8e3c210ce862a Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 19 Feb 2025 12:57:43 +0100 Subject: [PATCH 14/26] test: [ANDROAPP-6802] Implement optional validation rules --- .../composeResources/values-ar/strings.xml | 1 + .../composeResources/values-cs/strings.xml | 1 + .../composeResources/values-es/strings.xml | 1 + .../composeResources/values-fr/strings.xml | 1 + .../composeResources/values-id/strings.xml | 1 + .../composeResources/values-lo/strings.xml | 1 + .../composeResources/values-nb/strings.xml | 1 + .../composeResources/values-nl/strings.xml | 1 + .../composeResources/values-pt/strings.xml | 1 + .../composeResources/values-uk/strings.xml | 1 + .../composeResources/values-uz/strings.xml | 1 + .../composeResources/values-vi/strings.xml | 1 + .../values-zh-rCN/strings.xml | 1 + .../composeResources/values-zh/strings.xml | 1 + .../composeResources/values/strings.xml | 4 ++ .../ui/provider/DataSetModalDialogProvider.kt | 42 +++++++++++++++++++ .../aggregates/ui/provider/ResourceManager.kt | 9 ++++ .../ui/viewModel/DataSetTableViewModel.kt | 35 ++++++++++++---- 18 files changed, 97 insertions(+), 7 deletions(-) diff --git a/aggregates/src/commonMain/composeResources/values-ar/strings.xml b/aggregates/src/commonMain/composeResources/values-ar/strings.xml index 901ab117be..88b3b1553b 100644 --- a/aggregates/src/commonMain/composeResources/values-ar/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-ar/strings.xml @@ -8,5 +8,6 @@ إذا قمت بتعيين قيمة ، فإن الصف بأكمله يحتاج إلى كل القيم لإكمال مجموعة البيانات. موافق محفوظ ومكتمل + هل تريد التحقق من جودة البيانات؟ \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-cs/strings.xml b/aggregates/src/commonMain/composeResources/values-cs/strings.xml index abf1c8f303..bfbe5f874d 100644 --- a/aggregates/src/commonMain/composeResources/values-cs/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-cs/strings.xml @@ -8,5 +8,6 @@ Pokud nastavíte hodnotu, celý řádek potřebuje k dokončení datové sady všechny hodnoty. OK Uloženo a dokončeno! + Chcete zkontrolovat kvalitu dat? \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-es/strings.xml b/aggregates/src/commonMain/composeResources/values-es/strings.xml index e61c311f22..ea7f8a5fa1 100644 --- a/aggregates/src/commonMain/composeResources/values-es/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-es/strings.xml @@ -9,5 +9,6 @@ Si introduce un valor, tendrá que completar toda la fila para poder marcar como Completado el Data Set. Correcto ¡Guardado y completado! + ¿Quiere validar la calidad de los datos? diff --git a/aggregates/src/commonMain/composeResources/values-fr/strings.xml b/aggregates/src/commonMain/composeResources/values-fr/strings.xml index 2eb81f93bb..59eccebdff 100644 --- a/aggregates/src/commonMain/composeResources/values-fr/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-fr/strings.xml @@ -8,5 +8,6 @@ Si vous saisissez une valeur, la ligne entière doit être remplie pour compléter l\'ensemble de données. Ok Enregistré et terminé! + Voulez-vous vérifier la qualité des données? \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-id/strings.xml b/aggregates/src/commonMain/composeResources/values-id/strings.xml index 454e97f975..feb23fd6e0 100644 --- a/aggregates/src/commonMain/composeResources/values-id/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-id/strings.xml @@ -8,5 +8,6 @@ Jika Anda mengisi, semua baris harus terisi untuk menyelesaikan data set Ok Tersimpan dan selesai! + Apakah Anda ingin memeriksa kualitas data? \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-lo/strings.xml b/aggregates/src/commonMain/composeResources/values-lo/strings.xml index 312418f244..ac2267945b 100644 --- a/aggregates/src/commonMain/composeResources/values-lo/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-lo/strings.xml @@ -8,5 +8,6 @@ ຖ້າທ່ານຕັ້ງຄ່າ, ແຖວທັ້ງໝົດຕ້ອງໃຊ້ຄ່າທັ້ງໝົດເພື່ອເຮັດໃຫ້ຊຸດຂໍ້ມູນສົມບູນ ​ຕົກ​ລົງ ບັນ​ທຶກ​ແລະ​ສໍາ​ເລັດ​! + ທ່ານຕ້ອງການກວດສອບຄຸນນະພາບຂໍ້ມູນນີ້ບໍ? \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nb/strings.xml b/aggregates/src/commonMain/composeResources/values-nb/strings.xml index 8630caeda0..319f319a47 100644 --- a/aggregates/src/commonMain/composeResources/values-nb/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-nb/strings.xml @@ -8,5 +8,6 @@ Hvis du setter en verdi, så trenger hele raden alle verdier for å fullføre datasettet. OK Lagret og fullført! + Vil du sjekke datakvaliteten? \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nl/strings.xml b/aggregates/src/commonMain/composeResources/values-nl/strings.xml index 4c7542eb21..80504ebe01 100644 --- a/aggregates/src/commonMain/composeResources/values-nl/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-nl/strings.xml @@ -9,5 +9,6 @@ Als u een waarde instelt, heeft de hele rij alle waarden nodig om de gegevensset te voltooien. OK Opgeslagen en voltooid! + Wilt u de datakwaliteit controleren? \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-pt/strings.xml b/aggregates/src/commonMain/composeResources/values-pt/strings.xml index af8099df73..a0149cd471 100644 --- a/aggregates/src/commonMain/composeResources/values-pt/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-pt/strings.xml @@ -9,5 +9,6 @@ Se você definir um valor, a linha inteira precisa de todos os valores para concluir o conjunto de dados. Aprovado Salvo e concluído! + Você quer verificar a qualidade dos dados? diff --git a/aggregates/src/commonMain/composeResources/values-uk/strings.xml b/aggregates/src/commonMain/composeResources/values-uk/strings.xml index e0db0c759b..5d681241af 100644 --- a/aggregates/src/commonMain/composeResources/values-uk/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-uk/strings.xml @@ -9,5 +9,6 @@ Якщо Ви встановлюєте значення, то щодо усього рядка потрібно вказати всі значення, щоб завершити набір даних. ОК Збережено та завершено! + Хочете перевірити якість даних? \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-uz/strings.xml b/aggregates/src/commonMain/composeResources/values-uz/strings.xml index 5f391251e7..b23f048ca5 100644 --- a/aggregates/src/commonMain/composeResources/values-uz/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-uz/strings.xml @@ -8,5 +8,6 @@ Агар сиз қийматни ўрнатган бўлсангиз, маълумотлар тўпламини тўлдириш учун қатордаги барча қийматлар бўлиши лозим. ОК Якунланди ва сақланди! + Маълумотлар сифатини текширишни хохлайсизми? \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-vi/strings.xml b/aggregates/src/commonMain/composeResources/values-vi/strings.xml index ae1eb89fe7..e01e43210a 100644 --- a/aggregates/src/commonMain/composeResources/values-vi/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-vi/strings.xml @@ -9,5 +9,6 @@ Nếu bạn nhập một giá trị, cần nhập tất cả giá trị để hoàn tất Biểu Nhập OK Đã lưu và Hoàn tất! + Ban có muốn kiểm tra chất lượng dữ liệu không? \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml b/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml index 2d34d3d9d9..b80cd8d7f4 100644 --- a/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml @@ -8,5 +8,6 @@ 如果你设置值,整行的值需要设置才能完成数据集。 保存并完成! + 你要检查数据质量? \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh/strings.xml b/aggregates/src/commonMain/composeResources/values-zh/strings.xml index 9ac0893f9f..0fe04db475 100644 --- a/aggregates/src/commonMain/composeResources/values-zh/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-zh/strings.xml @@ -9,5 +9,6 @@ 如果你设置值,整行的值需要设置才能完成数据集。 保存并完成! + 您要检查数据质量吗? diff --git a/aggregates/src/commonMain/composeResources/values/strings.xml b/aggregates/src/commonMain/composeResources/values/strings.xml index 0899915614..f45aa12f34 100644 --- a/aggregates/src/commonMain/composeResources/values/strings.xml +++ b/aggregates/src/commonMain/composeResources/values/strings.xml @@ -28,4 +28,8 @@ OK Saved and completed! There has been an error completing the dataset + Do you want to check data quality? + No + Yes + \ No newline at end of file diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt index 3ff748b3c0..145856f2b0 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt @@ -89,4 +89,46 @@ internal class DataSetModalDialogProvider( onDismiss = onDismiss, ) } + + suspend fun provideAskRunValidationsDialog( + onDismiss: () -> Unit, + onDeny: () -> Unit, + onAccept: () -> Unit, + ): DataSetModalDialogUIState { + val denyText = resourceManager.provideNo() + val acceptText = resourceManager.provideYes() + + return DataSetModalDialogUIState( + contentDialogUIState = BottomSheetShellUIState( + title = resourceManager.provideSaved(), + subtitle = resourceManager.provideAskRunValidations(), + showBottomSectionDivider = false, + headerTextAlignment = TextAlign.Start, + ), + buttonsDialog = { + ButtonBlock( + modifier = Modifier.padding( + BottomSheetShellDefaults.buttonBlockPaddings(), + ), + primaryButton = { + Button( + style = ButtonStyle.OUTLINED, + text = denyText, + onClick = onDeny, + modifier = Modifier.fillMaxWidth(), + ) + }, + secondaryButton = { + Button( + style = ButtonStyle.FILLED, + text = acceptText, + modifier = Modifier.fillMaxWidth(), + onClick = onAccept, + ) + }, + ) + }, + onDismiss = onDismiss, + ) + } } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt index f1b3b54dc8..33e8b05417 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt @@ -9,10 +9,13 @@ import org.dhis2.mobile.aggregates.resources.error_on_complete_dataset import org.dhis2.mobile.aggregates.resources.field_mandatory import org.dhis2.mobile.aggregates.resources.field_required import org.dhis2.mobile.aggregates.resources.mark_dataset_complete +import org.dhis2.mobile.aggregates.resources.no import org.dhis2.mobile.aggregates.resources.not_now import org.dhis2.mobile.aggregates.resources.ok +import org.dhis2.mobile.aggregates.resources.run_validation_rules import org.dhis2.mobile.aggregates.resources.saved import org.dhis2.mobile.aggregates.resources.validation_success_title +import org.dhis2.mobile.aggregates.resources.yes import org.jetbrains.compose.resources.getString internal class ResourceManager { @@ -40,4 +43,10 @@ internal class ResourceManager { suspend fun provideSavedAndCompleted() = getString(Res.string.dataset_saved_completed) suspend fun provideErrorOnCompleteDataset() = getString(Res.string.error_on_complete_dataset) + + suspend fun provideAskRunValidations() = getString(Res.string.run_validation_rules) + + suspend fun provideNo() = getString(Res.string.no) + + suspend fun provideYes() = getString(Res.string.yes) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index 5e52575113..5af782e101 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope +import kotlinx.coroutines.withContext import org.dhis2.mobile.aggregates.domain.CheckCompletionStatus import org.dhis2.mobile.aggregates.domain.CheckValidationRulesConfiguration import org.dhis2.mobile.aggregates.domain.CompleteDataSet @@ -247,7 +248,25 @@ internal class DataSetTableViewModel( } OPTIONAL -> { - // TODO show validation rule dialog to ask if user wants to run validation rules + askRunValidationRules() + } + } + } + } + + private fun askRunValidationRules() { + viewModelScope.launch(dispatcher.io()) { + _dataSetScreenState.update { + if (it is DataSetScreenState.Loaded) { + it.copy( + modalDialog = datasetModalDialogProvider.provideAskRunValidationsDialog( + onDismiss = { onModalDialogDismissed() }, + onDeny = { attemptToComplete() }, + onAccept = { checkValidationRules() }, + ), + ) + } else { + it } } } @@ -340,8 +359,12 @@ internal class DataSetTableViewModel( } private fun onExit(exitMessage: String) { - showSnackbar(exitMessage) - onClose() + viewModelScope.launch { + withContext(dispatcher.main()) { + showSnackbar(exitMessage) + onClose() + } + } } private fun onModalDialogDismissed() { @@ -354,9 +377,7 @@ internal class DataSetTableViewModel( } } - private fun showSnackbar(message: String) { - viewModelScope.launch { - _dataSetScreenState.value.sendSnackbarMessage(message) - } + private suspend fun showSnackbar(message: String) { + _dataSetScreenState.value.sendSnackbarMessage(message) } } From 1e583fdd3e4bcbe22565cff3752bfb083232c503 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 20 Feb 2025 10:39:14 +0100 Subject: [PATCH 15/26] test: [ANDROAPP-6802] Add CompleteDataSetTest --- .../aggregates/domain/CompleteDataSetTest.kt | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSetTest.kt diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSetTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSetTest.kt new file mode 100644 index 0000000000..ba4ed4fc3a --- /dev/null +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSetTest.kt @@ -0,0 +1,159 @@ +package org.dhis2.mobile.aggregates.domain + +import kotlinx.coroutines.test.runTest +import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.ERROR +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.MISSING_MANDATORY_FIELDS_COMBINATION +import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.SUCCESS +import org.junit.Assert.assertEquals +import org.junit.Before +import org.mockito.Mockito.mock +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.whenever +import kotlin.test.Test + +class CompleteDataSetTest { + + private val dataSetInstanceRepository: DataSetInstanceRepository = mock() + + val dataSetUid = "dataSetUid" + val periodId = "periodId" + val orgUnitUid = "orgUnitUid" + val attrOptionComboUid = "attrOptionComboUid" + internal lateinit var completeDataSet: CompleteDataSet + + @Before + fun setUp() { + completeDataSet = CompleteDataSet( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + dataSetInstanceRepository, + ) + } + + @Test + fun `should return success when everything is right`() = runTest { + // Given dataset instance has no missing fields + whenever( + dataSetInstanceRepository.checkIfHasMissingMandatoryFields( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ), + ) doReturn false + + whenever( + dataSetInstanceRepository.checkIfHasMissingMandatoryFieldsCombination( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ), + ) doReturn false + + // And can be completed + whenever( + dataSetInstanceRepository.completeDataset( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ), + ) doReturn Result.success(Unit) + + // When user tries to complete the dataset + val result = completeDataSet() + + // Then completion is successful + assertEquals(SUCCESS, result) + } + + @Test + fun `should return error when there is any unhandled error`() = runTest { + // Given dataset instance has no missing fields + whenever( + dataSetInstanceRepository.checkIfHasMissingMandatoryFields( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ), + ) doReturn false + + whenever( + dataSetInstanceRepository.checkIfHasMissingMandatoryFieldsCombination( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ), + ) doReturn false + + // And has an unknown error + whenever( + dataSetInstanceRepository.completeDataset( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ), + ) doReturn Result.failure(Throwable()) + + // When user tries to complete the dataset + val result = completeDataSet() + + // Then completion fails + assertEquals(ERROR, result) + } + + @Test + fun `should return missing mandatory fields information`() = runTest { + // Given dataset instance has missing mandatory fields + whenever( + dataSetInstanceRepository.checkIfHasMissingMandatoryFields( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ), + ) doReturn true + + // When user tries to complete the dataset + val result = completeDataSet() + + // Then completion fails by missing mandatory fields + assertEquals(MISSING_MANDATORY_FIELDS, result) + } + + @Test + fun `should return missing mandatory fields combination information`() = runTest { + // Given dataset instance has missing fields combination + whenever( + dataSetInstanceRepository.checkIfHasMissingMandatoryFields( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ), + ) doReturn false + + whenever( + dataSetInstanceRepository.checkIfHasMissingMandatoryFieldsCombination( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + ), + ) doReturn true + + // When user tries to complete the dataset + val result = completeDataSet() + + // Then completion fails by missing fields combination + assertEquals(MISSING_MANDATORY_FIELDS_COMBINATION, result) + } +} From 955ad98e1daaba516af194d2b8d02b31533e081b Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 20 Feb 2025 11:08:42 +0100 Subject: [PATCH 16/26] test: [ANDROAPP-6802] Add CheckCompletionStatusTest --- .../domain/CheckCompletionStatusTest.kt | 72 +++++++++++++++++++ .../CheckValidationRulesConfigurationTest.kt | 34 +++++---- .../aggregates/domain/CompleteDataSetTest.kt | 8 +-- 3 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckCompletionStatusTest.kt diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckCompletionStatusTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckCompletionStatusTest.kt new file mode 100644 index 0000000000..2f2a493693 --- /dev/null +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckCompletionStatusTest.kt @@ -0,0 +1,72 @@ +package org.dhis2.mobile.aggregates.domain + +import kotlinx.coroutines.test.runTest +import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository +import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.COMPLETED +import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.NOT_COMPLETED +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.mock +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.whenever +import kotlin.test.assertEquals + +class CheckCompletionStatusTest { + + private val dataSetInstanceRepository: DataSetInstanceRepository = mock() + + private val dataSetUid = "dataSetUid" + private val periodId = "periodId" + private val orgUnitUid = "orgUnitUid" + private val attrOptionComboUid = "attrOptionComboUid" + internal lateinit var checkCompletionStatus: CheckCompletionStatus + + @Before + fun setUp() { + checkCompletionStatus = CheckCompletionStatus( + dataSetUid, + periodId, + orgUnitUid, + attrOptionComboUid, + dataSetInstanceRepository, + ) + } + + @Test + fun `should return dataset instance is completed`() = runTest { + // Given dataset instance is completed + whenever( + dataSetInstanceRepository.isComplete( + dataSetUid = dataSetUid, + periodId = periodId, + orgUnitUid = orgUnitUid, + attrOptionComboUid = attrOptionComboUid, + ), + ) doReturn true + + // When user check if dataset is completed + val result = checkCompletionStatus() + + // Then return completed + assertEquals(COMPLETED, result) + } + + @Test + fun `should return dataset instance is not completed`() = runTest { + // Given dataset instance is not completed + whenever( + dataSetInstanceRepository.isComplete( + dataSetUid = dataSetUid, + periodId = periodId, + orgUnitUid = orgUnitUid, + attrOptionComboUid = attrOptionComboUid, + ), + ) doReturn false + + // When user check if dataset is completed + val result = checkCompletionStatus() + + // Then return completed + assertEquals(NOT_COMPLETED, result) + } +} diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesConfigurationTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesConfigurationTest.kt index 3693db9014..918174b4d9 100644 --- a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesConfigurationTest.kt +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CheckValidationRulesConfigurationTest.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.test.runTest import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration import org.junit.Assert.assertEquals +import org.junit.Before import org.mockito.Mockito.mock import org.mockito.kotlin.doReturn import org.mockito.kotlin.whenever @@ -13,47 +14,50 @@ class CheckValidationRulesConfigurationTest { private val dataSetInstanceRepository: DataSetInstanceRepository = mock() + private val dataSetUid = "dataSetUid" + internal lateinit var checkValidationRulesConfiguration: CheckValidationRulesConfiguration + + @Before + fun setUp() { + checkValidationRulesConfiguration = + CheckValidationRulesConfiguration(dataSetUid, dataSetInstanceRepository) + } + @Test fun `should return NONE when there are no validation rules`() = runTest { - // Given - val dataSetUid = "dataSetUid" - val checkValidationRulesConfiguration = CheckValidationRulesConfiguration(dataSetUid, dataSetInstanceRepository) + // Given dataset has no validation rules whenever(dataSetInstanceRepository.checkIfHasValidationRules(dataSetUid)) doReturn false - // When + // When checking validation rules val result = checkValidationRulesConfiguration() - // Then + // Then return none assertEquals(ValidationRulesConfiguration.NONE, result) } @Test fun `should return Mandatory when validation rules are mandatory`() = runTest { - // Given - val dataSetUid = "dataSetUid" - val checkValidationRulesConfiguration = CheckValidationRulesConfiguration(dataSetUid, dataSetInstanceRepository) + // Given dataset has mandatory validation rules whenever(dataSetInstanceRepository.checkIfHasValidationRules(dataSetUid)) doReturn true whenever(dataSetInstanceRepository.areValidationRulesMandatory(dataSetUid)) doReturn true - // When + // When checking validation rules val result = checkValidationRulesConfiguration() - // Then + // Then return MANDATORY assertEquals(ValidationRulesConfiguration.MANDATORY, result) } @Test fun `should return Optional when validation rules are optional`() = runTest { - // Given - val dataSetUid = "dataSetUid" - val checkValidationRulesConfiguration = CheckValidationRulesConfiguration(dataSetUid, dataSetInstanceRepository) + // Given dataset has optional validation rules whenever(dataSetInstanceRepository.checkIfHasValidationRules(dataSetUid)) doReturn true whenever(dataSetInstanceRepository.areValidationRulesMandatory(dataSetUid)) doReturn false - // When + // When checking validation rules val result = checkValidationRulesConfiguration() - // Then + // Then return OPTIONAL assertEquals(ValidationRulesConfiguration.OPTIONAL, result) } } diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSetTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSetTest.kt index ba4ed4fc3a..a3050778b2 100644 --- a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSetTest.kt +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/domain/CompleteDataSetTest.kt @@ -17,10 +17,10 @@ class CompleteDataSetTest { private val dataSetInstanceRepository: DataSetInstanceRepository = mock() - val dataSetUid = "dataSetUid" - val periodId = "periodId" - val orgUnitUid = "orgUnitUid" - val attrOptionComboUid = "attrOptionComboUid" + private val dataSetUid = "dataSetUid" + private val periodId = "periodId" + private val orgUnitUid = "orgUnitUid" + private val attrOptionComboUid = "attrOptionComboUid" internal lateinit var completeDataSet: CompleteDataSet @Before From a6c9da02b90b3b17d9713e1d9dcd3d993c55a1af Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 20 Feb 2025 12:04:06 +0100 Subject: [PATCH 17/26] test: [ANDROAPP-6802] Add tests to DataSetTableViewModel --- .../ui/viewModel/DataSetTableViewModel.kt | 2 +- .../ui/viewModel/DataSetTableViewModelTest.kt | 121 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index 5af782e101..a2759a34d0 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -281,7 +281,7 @@ internal class DataSetTableViewModel( } ValidationResultStatus.ERROR -> { - // TODO show violations dialog + TODO("Violations dialog not implemented yet") } } } diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt index 4246e1a6dd..59cd225630 100644 --- a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.dhis2.mobile.aggregates.data.DataSetInstanceRepository @@ -25,6 +26,8 @@ import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.CellElement import org.dhis2.mobile.aggregates.model.CellInfo import org.dhis2.mobile.aggregates.domain.RunValidationRules +import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.COMPLETED +import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.NOT_COMPLETED import org.dhis2.mobile.aggregates.model.DataSetDetails import org.dhis2.mobile.aggregates.model.DataSetInstanceConfiguration import org.dhis2.mobile.aggregates.model.DataSetInstanceData @@ -34,12 +37,18 @@ import org.dhis2.mobile.aggregates.model.DataSetRenderingConfig import org.dhis2.mobile.aggregates.model.DataSetSection import org.dhis2.mobile.aggregates.model.InputType import org.dhis2.mobile.aggregates.model.TableGroup +import org.dhis2.mobile.aggregates.model.ValidationResultStatus +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.MANDATORY +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.NONE +import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.OPTIONAL +import org.dhis2.mobile.aggregates.model.ValidationRulesResult import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher import org.dhis2.mobile.aggregates.ui.provider.DataSetModalDialogProvider import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator import org.dhis2.mobile.aggregates.ui.inputs.TableId import org.dhis2.mobile.aggregates.ui.inputs.TableIdType +import org.dhis2.mobile.aggregates.ui.states.DataSetModalDialogUIState import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.dhis2.mobile.aggregates.ui.states.InputExtra @@ -55,8 +64,10 @@ import org.koin.test.mock.declareMock import org.mockito.Mockito.mock import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) internal class DataSetTableViewModelTest : KoinTest { @@ -78,6 +89,11 @@ internal class DataSetTableViewModelTest : KoinTest { private lateinit var completeDataSet: CompleteDataSet private lateinit var runValidationRules: RunValidationRules + private val onCloseCallback: () -> Unit = mock() + private val modalDialog: DataSetModalDialogUIState = mock() + + private lateinit var viewModel: DataSetTableViewModel + @Before fun setUp() = runTest { testDispatcher = StandardTestDispatcher(testScheduler) @@ -108,6 +124,7 @@ internal class DataSetTableViewModelTest : KoinTest { runValidationRules = declareMock() whenever(dispatcher.io).thenReturn { testDispatcher } + whenever(dispatcher.main).thenReturn { testDispatcher } whenever(getDataSetInstanceData(any())).thenReturn( DataSetInstanceData( dataSetDetails = DataSetDetails( @@ -166,6 +183,7 @@ internal class DataSetTableViewModelTest : KoinTest { whenever(getIndicators(any())).thenReturn(null) viewModel = DataSetTableViewModel( + onCloseCallback = onCloseCallback, getDataSetInstanceData = get(), getDataSetSectionData = get(), getDataValueData = get(), @@ -174,6 +192,9 @@ internal class DataSetTableViewModelTest : KoinTest { setDataValue = get(), resourceManager = get(), dispatcher = get(), + get(), + get(), + get(), ) } @@ -255,6 +276,106 @@ internal class DataSetTableViewModelTest : KoinTest { } } + @Test + fun `should finish a completed data set without validation rules`() = runTest { + // Given there are no validation rules + whenever(checkValidationRulesConfiguration()) doReturn NONE + // And data set instance is completed + whenever(checkCompletionStatus()) doReturn COMPLETED + + // When attempt to save + viewModel.onSaveClicked() + + // Then data set instance is closed + runCurrent() // Advance coroutine execution + verify(onCloseCallback).invoke() + } + + @Test + fun `should show complete dialog when no validation rules and uncompleted`() = runTest { + // Given there are no validation rules + whenever(checkValidationRulesConfiguration()) doReturn NONE + // And data set is not completed + whenever(checkCompletionStatus()) doReturn NOT_COMPLETED + + whenever( + dataSetModalDialogProvider.provideCompletionDialog( + any(), any(), any(), + ), + ) doReturn modalDialog + + viewModel.dataSetScreenState.test { + awaitInitialization() + + // When attempt to save + viewModel.onSaveClicked() + + // Then shows completion dialog + with(awaitItem()) { + assertTrue(this is DataSetScreenState.Loaded) + assertEquals(modalDialog, (this as DataSetScreenState.Loaded).modalDialog) + } + } + } + + @Test + fun `should ask to complete when running mandatory validation rules successfully`() = runTest { + // Given there are mandatory validation rules + whenever(checkValidationRulesConfiguration()) doReturn MANDATORY + // And validation rules execution is OK + val validationRulesResult = ValidationRulesResult( + validationResultStatus = ValidationResultStatus.OK, + violations = emptyList(), + ) + whenever(runValidationRules()) doReturn validationRulesResult + // And data set is not completed + whenever(checkCompletionStatus()) doReturn NOT_COMPLETED + + whenever( + dataSetModalDialogProvider.provideCompletionDialog( + any(), any(), any(), + ), + ) doReturn modalDialog + + viewModel.dataSetScreenState.test { + awaitInitialization() + + // When attempt to save + viewModel.onSaveClicked() + + // Then shows completion dialog + with(awaitItem()) { + assertTrue(this is DataSetScreenState.Loaded) + assertEquals(modalDialog, (this as DataSetScreenState.Loaded).modalDialog) + } + } + } + + @Test + fun `should ask to run optional validation rules`() = runTest { + // Given there are optional validation rules + whenever(checkValidationRulesConfiguration()) doReturn OPTIONAL + + whenever( + dataSetModalDialogProvider.provideAskRunValidationsDialog( + any(), any(), any(), + ), + ) doReturn modalDialog + + viewModel.dataSetScreenState.test { + awaitInitialization() + + // When attempt to save + viewModel.onSaveClicked() + + // Then shows optional validation rules dialog + with(awaitItem()) { + assertTrue(this is DataSetScreenState.Loaded) + assertEquals(modalDialog, (this as DataSetScreenState.Loaded).modalDialog) + } + } + } + private suspend fun ReceiveTurbine.awaitInitialization() = with(this) { awaitItem() awaitItem() From 73c45d624abff9d6179ef52dce7f40d89f24f40c Mon Sep 17 00:00:00 2001 From: andresmr Date: Fri, 21 Feb 2025 14:52:09 +0100 Subject: [PATCH 18/26] test: [ANDROAPP-6802] ValidationBar errors --- .../ui/ValidationRulesErrorDialogPreview.kt | 25 ++++ .../composeResources/values-ar/strings.xml | 2 + .../composeResources/values-cs/strings.xml | 2 + .../composeResources/values-es/strings.xml | 2 + .../composeResources/values-fr/strings.xml | 2 + .../composeResources/values-id/strings.xml | 2 + .../composeResources/values-lo/strings.xml | 1 + .../composeResources/values-nb/strings.xml | 2 + .../composeResources/values-nl/strings.xml | 2 + .../composeResources/values-pt/strings.xml | 2 + .../composeResources/values-ru/strings.xml | 1 + .../composeResources/values-uk/strings.xml | 2 + .../composeResources/values-uz/strings.xml | 2 + .../composeResources/values-vi/strings.xml | 2 + .../values-zh-rCN/strings.xml | 1 + .../composeResources/values-zh/strings.xml | 2 + .../composeResources/values/strings.xml | 4 +- .../aggregates/ui/DataSetTableScreen.kt | 11 +- .../aggregates/ui/component/ValidationBar.kt | 66 ++++++++++ .../component/ValidationRulesErrorDialog.kt | 120 ++++++++++++++++++ .../ui/provider/DataSetModalDialogProvider.kt | 42 ++++++ .../aggregates/ui/provider/ResourceManager.kt | 13 ++ .../ui/states/DataSetModalDialogUIState.kt | 1 + .../ui/states/DataSetScreenState.kt | 1 + .../ui/states/ValidationBarUiState.kt | 7 + .../ui/viewModel/DataSetTableViewModel.kt | 49 ++++++- 26 files changed, 361 insertions(+), 5 deletions(-) create mode 100644 aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationBar.kt create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/ValidationBarUiState.kt diff --git a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt new file mode 100644 index 0000000000..51320975f4 --- /dev/null +++ b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt @@ -0,0 +1,25 @@ +package org.dhis2.mobile.aggregates.ui + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import org.dhis2.mobile.aggregates.ui.component.ValidationBar +import org.dhis2.mobile.aggregates.ui.component.ValidationRulesErrorDialog +import org.dhis2.mobile.aggregates.ui.states.ValidationBarUiState + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun ValidationRulesErrorDialogPreview() { + ValidationRulesErrorDialog() +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun ValidationBarPreview() { + ValidationBar( + ValidationBarUiState( + quantity = 1, + description = "description", + onExpandErrors = {}, + ), + ) +} diff --git a/aggregates/src/commonMain/composeResources/values-ar/strings.xml b/aggregates/src/commonMain/composeResources/values-ar/strings.xml index 88b3b1553b..bd604d9872 100644 --- a/aggregates/src/commonMain/composeResources/values-ar/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-ar/strings.xml @@ -9,5 +9,7 @@ موافق محفوظ ومكتمل هل تريد التحقق من جودة البيانات؟ + خطأ + إكمال على أية حال \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-cs/strings.xml b/aggregates/src/commonMain/composeResources/values-cs/strings.xml index bfbe5f874d..a1b505656f 100644 --- a/aggregates/src/commonMain/composeResources/values-cs/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-cs/strings.xml @@ -9,5 +9,7 @@ OK Uloženo a dokončeno! Chcete zkontrolovat kvalitu dat? + Chyba + Přesto dokončit \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-es/strings.xml b/aggregates/src/commonMain/composeResources/values-es/strings.xml index ea7f8a5fa1..30b281ab0b 100644 --- a/aggregates/src/commonMain/composeResources/values-es/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-es/strings.xml @@ -10,5 +10,7 @@ Correcto ¡Guardado y completado! ¿Quiere validar la calidad de los datos? + Error + Completar de todas formas diff --git a/aggregates/src/commonMain/composeResources/values-fr/strings.xml b/aggregates/src/commonMain/composeResources/values-fr/strings.xml index 59eccebdff..7b0fa362be 100644 --- a/aggregates/src/commonMain/composeResources/values-fr/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-fr/strings.xml @@ -9,5 +9,7 @@ Ok Enregistré et terminé! Voulez-vous vérifier la qualité des données? + Erreur + Terminer quand même \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-id/strings.xml b/aggregates/src/commonMain/composeResources/values-id/strings.xml index feb23fd6e0..75c4afd544 100644 --- a/aggregates/src/commonMain/composeResources/values-id/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-id/strings.xml @@ -9,5 +9,7 @@ Ok Tersimpan dan selesai! Apakah Anda ingin memeriksa kualitas data? + Kesalahan + Lengkap \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-lo/strings.xml b/aggregates/src/commonMain/composeResources/values-lo/strings.xml index ac2267945b..3ee4fa4036 100644 --- a/aggregates/src/commonMain/composeResources/values-lo/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-lo/strings.xml @@ -9,5 +9,6 @@ ​ຕົກ​ລົງ ບັນ​ທຶກ​ແລະ​ສໍາ​ເລັດ​! ທ່ານຕ້ອງການກວດສອບຄຸນນະພາບຂໍ້ມູນນີ້ບໍ? + ເກີດການຜິດພາດ \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nb/strings.xml b/aggregates/src/commonMain/composeResources/values-nb/strings.xml index 319f319a47..f17a88d6f6 100644 --- a/aggregates/src/commonMain/composeResources/values-nb/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-nb/strings.xml @@ -9,5 +9,7 @@ OK Lagret og fullført! Vil du sjekke datakvaliteten? + Feil + Fullfør likevel \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nl/strings.xml b/aggregates/src/commonMain/composeResources/values-nl/strings.xml index 80504ebe01..fb895c7fe5 100644 --- a/aggregates/src/commonMain/composeResources/values-nl/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-nl/strings.xml @@ -10,5 +10,7 @@ OK Opgeslagen en voltooid! Wilt u de datakwaliteit controleren? + Fout + In ieder geval compleet \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-pt/strings.xml b/aggregates/src/commonMain/composeResources/values-pt/strings.xml index a0149cd471..a9dec93964 100644 --- a/aggregates/src/commonMain/composeResources/values-pt/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-pt/strings.xml @@ -10,5 +10,7 @@ Aprovado Salvo e concluído! Você quer verificar a qualidade dos dados? + Erro + Completar mesmo assim diff --git a/aggregates/src/commonMain/composeResources/values-ru/strings.xml b/aggregates/src/commonMain/composeResources/values-ru/strings.xml index 531b3729ed..fb341e1e13 100644 --- a/aggregates/src/commonMain/composeResources/values-ru/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-ru/strings.xml @@ -2,5 +2,6 @@ Завершенный ОК + Ошибка diff --git a/aggregates/src/commonMain/composeResources/values-uk/strings.xml b/aggregates/src/commonMain/composeResources/values-uk/strings.xml index 5d681241af..2ad5eeb6fc 100644 --- a/aggregates/src/commonMain/composeResources/values-uk/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-uk/strings.xml @@ -10,5 +10,7 @@ ОК Збережено та завершено! Хочете перевірити якість даних? + Помилка + Все одно завершити \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-uz/strings.xml b/aggregates/src/commonMain/composeResources/values-uz/strings.xml index b23f048ca5..0bfb09e32d 100644 --- a/aggregates/src/commonMain/composeResources/values-uz/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-uz/strings.xml @@ -9,5 +9,7 @@ ОК Якунланди ва сақланди! Маълумотлар сифатини текширишни хохлайсизми? + Хатолик + Барибир тўлдиринг \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-vi/strings.xml b/aggregates/src/commonMain/composeResources/values-vi/strings.xml index e01e43210a..ae5cd102f0 100644 --- a/aggregates/src/commonMain/composeResources/values-vi/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-vi/strings.xml @@ -10,5 +10,7 @@ OK Đã lưu và Hoàn tất! Ban có muốn kiểm tra chất lượng dữ liệu không? + Lỗi + Hoàn tất luôn \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml b/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml index b80cd8d7f4..e44474bbe1 100644 --- a/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml @@ -9,5 +9,6 @@ 保存并完成! 你要检查数据质量? + 任何都要完成 \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh/strings.xml b/aggregates/src/commonMain/composeResources/values-zh/strings.xml index 0fe04db475..b9ab81ec35 100644 --- a/aggregates/src/commonMain/composeResources/values-zh/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-zh/strings.xml @@ -10,5 +10,7 @@ 保存并完成! 您要检查数据质量吗? + 错误 + 任何都要完成 diff --git a/aggregates/src/commonMain/composeResources/values/strings.xml b/aggregates/src/commonMain/composeResources/values/strings.xml index f45aa12f34..642b7ab380 100644 --- a/aggregates/src/commonMain/composeResources/values/strings.xml +++ b/aggregates/src/commonMain/composeResources/values/strings.xml @@ -31,5 +31,7 @@ Do you want to check data quality? No Yes - + Error + Errors + Complete anyway \ No newline at end of file diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt index 79c28d1468..1b899947e0 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt @@ -1,4 +1,4 @@ -@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) +@file:OptIn(ExperimentalMaterial3Api::class) package org.dhis2.mobile.aggregates.ui @@ -8,7 +8,6 @@ import androidx.compose.animation.slideOutVertically import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Box @@ -57,6 +56,7 @@ import org.dhis2.mobile.aggregates.ui.constants.INPUT_DIALOG_DONE_TAG import org.dhis2.mobile.aggregates.ui.constants.INPUT_DIALOG_TAG import org.dhis2.mobile.aggregates.ui.constants.SYNC_BUTTON_TAG import org.dhis2.mobile.aggregates.ui.inputs.InputProvider +import org.dhis2.mobile.aggregates.ui.component.ValidationBar import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.dhis2.mobile.aggregates.ui.viewModel.DataSetTableViewModel @@ -199,6 +199,11 @@ fun DataSetInstanceScreen( ) } }, + bottomBar = { + (dataSetScreenState as? DataSetScreenState.Loaded)?.validationBar?.let { validationBarUiState -> + ValidationBar(uiState = validationBarUiState) + } + }, ) { Box( modifier = Modifier.fillMaxSize() @@ -338,7 +343,7 @@ fun DataSetInstanceScreen( (dataSetScreenState as? DataSetScreenState.Loaded)?.modalDialog?.let { dataSetUIState -> BottomSheetShell( uiState = dataSetUIState.contentDialogUIState, - content = null, + content = dataSetUIState.content, buttonBlock = dataSetUIState.buttonsDialog, onDismiss = dataSetUIState.onDismiss, ) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationBar.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationBar.kt new file mode 100644 index 0000000000..adb740d1ae --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationBar.kt @@ -0,0 +1,66 @@ +package org.dhis2.mobile.aggregates.ui.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ExpandLess +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import org.dhis2.mobile.aggregates.ui.states.ValidationBarUiState +import org.hisp.dhis.mobile.ui.designsystem.component.Badge +import org.hisp.dhis.mobile.ui.designsystem.component.IconButton +import org.hisp.dhis.mobile.ui.designsystem.component.IconButtonStyle +import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor +import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor + +@Composable +internal fun ValidationBar( + uiState: ValidationBarUiState, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(Spacing.Spacing8), + modifier = Modifier + .fillMaxWidth() + .background(color = SurfaceColor.ErrorContainer) + .padding( + start = Spacing.Spacing16, + end = Spacing.Spacing4, + ), + ) { + Badge( + modifier = Modifier + .padding( + start = Spacing.Spacing4, + end = Spacing.Spacing4, + ), + text = uiState.quantity.toString(), + color = SurfaceColor.Error, + textColor = TextColor.OnPrimary, + ) + Text( + modifier = Modifier.weight(1f), + text = uiState.description, + style = MaterialTheme.typography.bodyMedium, + ) + IconButton( + style = IconButtonStyle.STANDARD, + icon = { + Icon( + imageVector = Icons.Outlined.ExpandLess, + contentDescription = "Expand", + tint = TextColor.OnSurfaceVariant, + ) + }, + onClick = uiState.onExpandErrors, + ) + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt new file mode 100644 index 0000000000..2b43ba5418 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt @@ -0,0 +1,120 @@ +package org.dhis2.mobile.aggregates.ui.component + +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import org.hisp.dhis.mobile.ui.designsystem.theme.Shape +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor +import kotlin.math.max + +@Composable +fun ValidationRulesErrorDialog() { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + val pageCount = 2 + val pagerState = rememberPagerState( + pageCount = { pageCount }, + ) + PagerIndicator( + pageCount = pageCount, + currentPage = pagerState.currentPage, + ) + + HorizontalPager( + state = pagerState, + modifier = Modifier + .fillMaxSize() + .weight(1f) + .clip(Shape.Medium), + ) { + Box( + modifier = Modifier + .fillMaxSize() + .background(SurfaceColor.Surface), + contentAlignment = Alignment.Center, + ) { + Text(text = "Page $it") + } + } + } +} + +@Composable +internal fun PagerIndicator( + indicatorScrollState: LazyListState = rememberLazyListState(), + pageCount: Int, + currentPage: Int, + dotIndicatorColor: Color = SurfaceColor.Error, +) { + LaunchedEffect(key1 = currentPage) { + val size = indicatorScrollState.layoutInfo.visibleItemsInfo.size + val lastVisibleIndex = + indicatorScrollState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0 + val firstVisibleItemIndex = indicatorScrollState.firstVisibleItemIndex + + if (currentPage > lastVisibleIndex - 1) { + indicatorScrollState.animateScrollToItem(currentPage - size + 2) + } else if (currentPage <= firstVisibleItemIndex + 1) { + indicatorScrollState.animateScrollToItem(max(currentPage - 1, 0)) + } + } + + LazyRow( + state = indicatorScrollState, + modifier = Modifier + .height(50.dp) + .width(((6 + 16) * 2 + 3 * (10 + 16)).dp), // I'm hard computing it to simplify + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + repeat(pageCount) { iteration -> + val color = if (currentPage == iteration) dotIndicatorColor else Color.LightGray + item(key = "item$iteration") { + val firstVisibleIndex by remember { derivedStateOf { indicatorScrollState.firstVisibleItemIndex } } + val lastVisibleIndex = + indicatorScrollState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0 + val size by animateDpAsState( + targetValue = when (iteration) { + currentPage -> 10.dp + in firstVisibleIndex + 1.. 10.dp + else -> 6.dp + }, + label = "PagerIndicatorDotSizeAnimation", + ) + Box( + modifier = Modifier + .padding(all = 8.dp) + .background(color = color, CircleShape) + .size( + size, + ), + ) + } + } + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt index 145856f2b0..ea7ebad66b 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt @@ -4,6 +4,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign +import org.dhis2.mobile.aggregates.model.Violation +import org.dhis2.mobile.aggregates.ui.component.ValidationRulesErrorDialog import org.dhis2.mobile.aggregates.ui.states.DataSetModalDialogUIState import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonBlock @@ -131,4 +133,44 @@ internal class DataSetModalDialogProvider( onDismiss = onDismiss, ) } + + suspend fun provideValidationRulesErrorDialog( + onDismiss: () -> Unit, + onMarkAsComplete: () -> Unit, + violations: List, + ): DataSetModalDialogUIState { + val completeAnywayText = resourceManager.provideCompleteAnyway() + + return DataSetModalDialogUIState( + contentDialogUIState = BottomSheetShellUIState( + title = "${violations.size} ${ + resourceManager.provideValidationErrorDescription( + violations.size, + ) + }", + showTopSectionDivider = false, + showBottomSectionDivider = false, + headerTextAlignment = TextAlign.Start, + ), + content = { + ValidationRulesErrorDialog() + }, + buttonsDialog = { + ButtonBlock( + modifier = Modifier.padding( + BottomSheetShellDefaults.buttonBlockPaddings(), + ), + primaryButton = {}, + secondaryButton = { + Button( + style = ButtonStyle.TEXT, + text = completeAnywayText, + onClick = onMarkAsComplete, + ) + }, + ) + }, + onDismiss = onDismiss, + ) + } } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt index 33e8b05417..6aef9683c2 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt @@ -2,10 +2,13 @@ package org.dhis2.mobile.aggregates.ui.provider import org.dhis2.mobile.aggregates.resources.Res import org.dhis2.mobile.aggregates.resources.complete +import org.dhis2.mobile.aggregates.resources.complete_anyway import org.dhis2.mobile.aggregates.resources.dataset_saved_completed import org.dhis2.mobile.aggregates.resources.default_column_label import org.dhis2.mobile.aggregates.resources.total_header_label +import org.dhis2.mobile.aggregates.resources.error import org.dhis2.mobile.aggregates.resources.error_on_complete_dataset +import org.dhis2.mobile.aggregates.resources.errors import org.dhis2.mobile.aggregates.resources.field_mandatory import org.dhis2.mobile.aggregates.resources.field_required import org.dhis2.mobile.aggregates.resources.mark_dataset_complete @@ -49,4 +52,14 @@ internal class ResourceManager { suspend fun provideNo() = getString(Res.string.no) suspend fun provideYes() = getString(Res.string.yes) + + suspend fun provideValidationErrorDescription(errors: Int): String { + return if (errors == 1) { + getString(Res.string.error) + } else { + getString(Res.string.errors) + } + } + + suspend fun provideCompleteAnyway() = getString(Res.string.complete_anyway) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt index 25df5bcc67..7281628c32 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt @@ -5,6 +5,7 @@ import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellUISt internal data class DataSetModalDialogUIState( val contentDialogUIState: BottomSheetShellUIState, + val content: @Composable (() -> Unit)? = null, val buttonsDialog: @Composable (() -> Unit), val onDismiss: () -> Unit, ) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt index bee6b9d44f..4eb33264f5 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt @@ -23,6 +23,7 @@ internal sealed class DataSetScreenState { val dataSetSectionTable: DataSetSectionTable, val selectedCellInfo: InputData? = null, val modalDialog: DataSetModalDialogUIState? = null, + val validationBar: ValidationBarUiState? = null, ) : DataSetScreenState() { override fun allowTwoPane(canUseTwoPane: Boolean) = dataSetSections.isNotEmpty() && canUseTwoPane && renderingConfig.useVerticalTabs diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/ValidationBarUiState.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/ValidationBarUiState.kt new file mode 100644 index 0000000000..076f997f26 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/ValidationBarUiState.kt @@ -0,0 +1,7 @@ +package org.dhis2.mobile.aggregates.ui.states + +internal data class ValidationBarUiState( + val quantity: Int, + val description: String, + val onExpandErrors: () -> Unit, +) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index a2759a34d0..c6edfd3edf 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -32,6 +32,7 @@ import org.dhis2.mobile.aggregates.model.ValidationResultStatus import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.MANDATORY import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.NONE import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.OPTIONAL +import org.dhis2.mobile.aggregates.model.Violation import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.mapper.toInputData import org.dhis2.mobile.aggregates.model.mapper.toTableModel @@ -45,6 +46,12 @@ import org.dhis2.mobile.aggregates.ui.provider.DataSetModalDialogProvider import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable +import org.dhis2.mobile.aggregates.ui.states.ValidationBarUiState +import org.hisp.dhis.mobile.ui.designsystem.component.table.model.RowHeader +import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableCell +import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableHeader +import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableHeaderCell +import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableHeaderRow import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableModel internal class DataSetTableViewModel( @@ -277,11 +284,51 @@ internal class DataSetTableViewModel( val rules = runValidationRules() when (rules.validationResultStatus) { ValidationResultStatus.OK -> { + _dataSetScreenState.update { + if (it is DataSetScreenState.Loaded) { + it.copy(validationBar = null) + } else { + it + } + } attemptToFinnish() } ValidationResultStatus.ERROR -> { - TODO("Violations dialog not implemented yet") + onModalDialogDismissed() + _dataSetScreenState.update { + if (it is DataSetScreenState.Loaded) { + it.copy( + validationBar = ValidationBarUiState( + quantity = rules.violations.size, + description = resourceManager.provideValidationErrorDescription( + errors = rules.violations.size, + ), + onExpandErrors = { expandValidationErrors(rules.violations) }, + ), + ) + } else { + it + } + } + } + } + } + } + + private fun expandValidationErrors(violations: List) { + viewModelScope.launch(dispatcher.main()) { + _dataSetScreenState.update { + if (it is DataSetScreenState.Loaded) { + it.copy( + modalDialog = datasetModalDialogProvider.provideValidationRulesErrorDialog( + violations = violations, + onDismiss = { onModalDialogDismissed() }, + onMarkAsComplete = { attemptToComplete() }, + ), + ) + } else { + it } } } From e493cf8126dfbdf2ff9525edf39baef17f6d4bd7 Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 24 Feb 2025 15:57:12 +0100 Subject: [PATCH 19/26] test: [ANDROAPP-6802] ValidationBar violations --- .../ui/ValidationRulesErrorDialogPreview.kt | 16 +++- .../composeResources/values-cs/strings.xml | 1 + .../composeResources/values-es/strings.xml | 1 + .../composeResources/values-fr/strings.xml | 1 + .../composeResources/values-lo/strings.xml | 1 + .../composeResources/values-nl/strings.xml | 1 + .../composeResources/values-pt/strings.xml | 1 + .../composeResources/values-uk/strings.xml | 2 +- .../composeResources/values-vi/strings.xml | 1 + .../composeResources/values-zh/strings.xml | 1 + .../composeResources/values/strings.xml | 1 + .../aggregates/model/ValidationRulesResult.kt | 2 +- .../aggregates/ui/DataSetTableScreen.kt | 3 +- .../component/ValidationRulesErrorDialog.kt | 91 +++++++++++++++---- .../ui/provider/DataSetModalDialogProvider.kt | 27 +++++- .../aggregates/ui/provider/ResourceManager.kt | 3 + .../ui/states/DataSetModalDialogUIState.kt | 1 + 17 files changed, 130 insertions(+), 24 deletions(-) diff --git a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt index 51320975f4..f8b67ea5e5 100644 --- a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt +++ b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt @@ -2,6 +2,7 @@ package org.dhis2.mobile.aggregates.ui import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview +import org.dhis2.mobile.aggregates.model.Violation import org.dhis2.mobile.aggregates.ui.component.ValidationBar import org.dhis2.mobile.aggregates.ui.component.ValidationRulesErrorDialog import org.dhis2.mobile.aggregates.ui.states.ValidationBarUiState @@ -9,7 +10,20 @@ import org.dhis2.mobile.aggregates.ui.states.ValidationBarUiState @Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) @Composable fun ValidationRulesErrorDialogPreview() { - ValidationRulesErrorDialog() + ValidationRulesErrorDialog( + listOf( + Violation( + "Please adjust the Chickenpox total to match the combined counts.", + "CPTotal = CPMale + CPFemale", + emptyList(), + ), + Violation( + "description", + "field", + emptyList(), + ), + ), + ) } @Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) diff --git a/aggregates/src/commonMain/composeResources/values-cs/strings.xml b/aggregates/src/commonMain/composeResources/values-cs/strings.xml index a1b505656f..7e345c71b9 100644 --- a/aggregates/src/commonMain/composeResources/values-cs/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-cs/strings.xml @@ -11,5 +11,6 @@ Chcete zkontrolovat kvalitu dat? Chyba Přesto dokončit + Posouzení \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-es/strings.xml b/aggregates/src/commonMain/composeResources/values-es/strings.xml index 30b281ab0b..a2c3f972ca 100644 --- a/aggregates/src/commonMain/composeResources/values-es/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-es/strings.xml @@ -12,5 +12,6 @@ ¿Quiere validar la calidad de los datos? Error Completar de todas formas + Revisar diff --git a/aggregates/src/commonMain/composeResources/values-fr/strings.xml b/aggregates/src/commonMain/composeResources/values-fr/strings.xml index 7b0fa362be..1182ef6dde 100644 --- a/aggregates/src/commonMain/composeResources/values-fr/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-fr/strings.xml @@ -11,5 +11,6 @@ Voulez-vous vérifier la qualité des données? Erreur Terminer quand même + Vérifier \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-lo/strings.xml b/aggregates/src/commonMain/composeResources/values-lo/strings.xml index 3ee4fa4036..86fb21efb9 100644 --- a/aggregates/src/commonMain/composeResources/values-lo/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-lo/strings.xml @@ -10,5 +10,6 @@ ບັນ​ທຶກ​ແລະ​ສໍາ​ເລັດ​! ທ່ານຕ້ອງການກວດສອບຄຸນນະພາບຂໍ້ມູນນີ້ບໍ? ເກີດການຜິດພາດ + ທົບທວນຄືນ \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nl/strings.xml b/aggregates/src/commonMain/composeResources/values-nl/strings.xml index fb895c7fe5..1a0ed667d7 100644 --- a/aggregates/src/commonMain/composeResources/values-nl/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-nl/strings.xml @@ -12,5 +12,6 @@ Wilt u de datakwaliteit controleren? Fout In ieder geval compleet + Beoordeling \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-pt/strings.xml b/aggregates/src/commonMain/composeResources/values-pt/strings.xml index a9dec93964..7f32231fcd 100644 --- a/aggregates/src/commonMain/composeResources/values-pt/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-pt/strings.xml @@ -12,5 +12,6 @@ Você quer verificar a qualidade dos dados? Erro Completar mesmo assim + Revisão diff --git a/aggregates/src/commonMain/composeResources/values-uk/strings.xml b/aggregates/src/commonMain/composeResources/values-uk/strings.xml index 2ad5eeb6fc..9bcbd64cc0 100644 --- a/aggregates/src/commonMain/composeResources/values-uk/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-uk/strings.xml @@ -12,5 +12,5 @@ Хочете перевірити якість даних? Помилка Все одно завершити - + Перегляд \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-vi/strings.xml b/aggregates/src/commonMain/composeResources/values-vi/strings.xml index ae5cd102f0..26f17cae94 100644 --- a/aggregates/src/commonMain/composeResources/values-vi/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-vi/strings.xml @@ -12,5 +12,6 @@ Ban có muốn kiểm tra chất lượng dữ liệu không? Lỗi Hoàn tất luôn + Xem xét \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh/strings.xml b/aggregates/src/commonMain/composeResources/values-zh/strings.xml index b9ab81ec35..891735444d 100644 --- a/aggregates/src/commonMain/composeResources/values-zh/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-zh/strings.xml @@ -12,5 +12,6 @@ 您要检查数据质量吗? 错误 任何都要完成 + 审查 diff --git a/aggregates/src/commonMain/composeResources/values/strings.xml b/aggregates/src/commonMain/composeResources/values/strings.xml index 642b7ab380..468e9d8d7e 100644 --- a/aggregates/src/commonMain/composeResources/values/strings.xml +++ b/aggregates/src/commonMain/composeResources/values/strings.xml @@ -34,4 +34,5 @@ Error Errors Complete anyway + Review \ No newline at end of file diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesResult.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesResult.kt index eb440e347e..3fb054b633 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesResult.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/ValidationRulesResult.kt @@ -24,7 +24,7 @@ internal data class DataToReview( dataElementDisplayName ?: dataElementUid } else { String.format( - "%s | %s", + "%s / %s", dataElementDisplayName, categoryOptionComboDisplayName, ) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt index 1b899947e0..2b62e61f49 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt @@ -181,7 +181,7 @@ fun DataSetInstanceScreen( snackbarHost = { SnackbarHost(snackbarHostState) }, floatingActionButton = { AnimatedVisibility( - visible = true, + visible = (dataSetScreenState is DataSetScreenState.Loaded), enter = fadeIn(), exit = fadeOut(), ) { @@ -346,6 +346,7 @@ fun DataSetInstanceScreen( content = dataSetUIState.content, buttonBlock = dataSetUIState.buttonsDialog, onDismiss = dataSetUIState.onDismiss, + icon = dataSetUIState.icon, ) } } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt index 2b43ba5418..b2ff691429 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt @@ -3,19 +3,25 @@ package org.dhis2.mobile.aggregates.ui.component import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -26,39 +32,91 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import org.dhis2.mobile.aggregates.model.Violation +import org.hisp.dhis.mobile.ui.designsystem.component.Tag +import org.hisp.dhis.mobile.ui.designsystem.component.TagType import org.hisp.dhis.mobile.ui.designsystem.theme.Shape +import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor import kotlin.math.max @Composable -fun ValidationRulesErrorDialog() { +internal fun ValidationRulesErrorDialog(violations: List) { Column( horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = spacedBy(Spacing.Spacing24), ) { - val pageCount = 2 + val pageCount = violations.size val pagerState = rememberPagerState( pageCount = { pageCount }, ) - PagerIndicator( - pageCount = pageCount, - currentPage = pagerState.currentPage, - ) + + if (pageCount > 1) { + PagerIndicator( + pageCount = pageCount, + currentPage = pagerState.currentPage, + ) + } HorizontalPager( state = pagerState, modifier = Modifier .fillMaxSize() .weight(1f) - .clip(Shape.Medium), - ) { - Box( + .clip(Shape.Large), + pageSpacing = Spacing.Spacing4, + ) { page -> + val violation = violations[page] + + LazyColumn( modifier = Modifier .fillMaxSize() - .background(SurfaceColor.Surface), - contentAlignment = Alignment.Center, + .background(SurfaceColor.PrimaryContainer) + .padding(Spacing.Spacing16), ) { - Text(text = "Page $it") + item { + Column( + verticalArrangement = spacedBy(Spacing.Spacing8), + ) { + Text( + text = violation.description ?: "", + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.Bold, + ) + Text( + text = violation.instruction ?: "", + style = MaterialTheme.typography.bodyMedium, + ) + } + } + + item { + Spacer(modifier = Modifier.height(Spacing.Spacing24)) + } + item { + Text( + text = "Data to review", + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.Bold, + ) + } + items(violation.dataToReview) { dataToReview -> + Spacer(modifier = Modifier.height(Spacing.Spacing8)) + Row( + horizontalArrangement = spacedBy(Spacing.Spacing4), + ) { + Text( + text = "${dataToReview.formattedDataLabel()}:", + style = MaterialTheme.typography.bodyMedium, + ) + Tag( + label = dataToReview.value, + type = TagType.ERROR, + ) + } + } } } } @@ -87,22 +145,23 @@ internal fun PagerIndicator( LazyRow( state = indicatorScrollState, modifier = Modifier - .height(50.dp) +// .height(50.dp) .width(((6 + 16) * 2 + 3 * (10 + 16)).dp), // I'm hard computing it to simplify horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { repeat(pageCount) { iteration -> - val color = if (currentPage == iteration) dotIndicatorColor else Color.LightGray + val color = + if (currentPage == iteration) dotIndicatorColor else SurfaceColor.ErrorContainer item(key = "item$iteration") { val firstVisibleIndex by remember { derivedStateOf { indicatorScrollState.firstVisibleItemIndex } } val lastVisibleIndex = indicatorScrollState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0 val size by animateDpAsState( targetValue = when (iteration) { - currentPage -> 10.dp + currentPage -> Spacing.Spacing14 in firstVisibleIndex + 1.. 10.dp - else -> 6.dp + else -> Spacing.Spacing14 }, label = "PagerIndicatorDotSizeAnimation", ) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt index ea7ebad66b..f85607b4b4 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt @@ -2,6 +2,10 @@ package org.dhis2.mobile.aggregates.ui.provider import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Error +import androidx.compose.material.icons.outlined.ErrorOutline +import androidx.compose.material3.Icon import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import org.dhis2.mobile.aggregates.model.Violation @@ -12,6 +16,7 @@ import org.hisp.dhis.mobile.ui.designsystem.component.ButtonBlock import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellDefaults import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellUIState +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor internal class DataSetModalDialogProvider( val resourceManager: ResourceManager, @@ -140,6 +145,7 @@ internal class DataSetModalDialogProvider( violations: List, ): DataSetModalDialogUIState { val completeAnywayText = resourceManager.provideCompleteAnyway() + val reviewText = resourceManager.provideReview() return DataSetModalDialogUIState( contentDialogUIState = BottomSheetShellUIState( @@ -150,24 +156,37 @@ internal class DataSetModalDialogProvider( }", showTopSectionDivider = false, showBottomSectionDivider = false, - headerTextAlignment = TextAlign.Start, ), content = { - ValidationRulesErrorDialog() + ValidationRulesErrorDialog(violations) + }, + icon = { + Icon( + imageVector = Icons.Outlined.ErrorOutline, + contentDescription = null, + tint = SurfaceColor.Error, + ) }, buttonsDialog = { ButtonBlock( modifier = Modifier.padding( BottomSheetShellDefaults.buttonBlockPaddings(), ), - primaryButton = {}, - secondaryButton = { + primaryButton = { Button( style = ButtonStyle.TEXT, text = completeAnywayText, onClick = onMarkAsComplete, ) }, + secondaryButton = { + Button( + style = ButtonStyle.FILLED, + text = reviewText, + modifier = Modifier.fillMaxWidth(), + onClick = onDismiss, + ) + }, ) }, onDismiss = onDismiss, diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt index 6aef9683c2..5c1a336c56 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt @@ -15,6 +15,7 @@ import org.dhis2.mobile.aggregates.resources.mark_dataset_complete import org.dhis2.mobile.aggregates.resources.no import org.dhis2.mobile.aggregates.resources.not_now import org.dhis2.mobile.aggregates.resources.ok +import org.dhis2.mobile.aggregates.resources.review import org.dhis2.mobile.aggregates.resources.run_validation_rules import org.dhis2.mobile.aggregates.resources.saved import org.dhis2.mobile.aggregates.resources.validation_success_title @@ -62,4 +63,6 @@ internal class ResourceManager { } suspend fun provideCompleteAnyway() = getString(Res.string.complete_anyway) + + suspend fun provideReview() = getString(Res.string.review) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt index 7281628c32..d660c56250 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt @@ -6,6 +6,7 @@ import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellUISt internal data class DataSetModalDialogUIState( val contentDialogUIState: BottomSheetShellUIState, val content: @Composable (() -> Unit)? = null, + val icon: @Composable (() -> Unit)? = null, val buttonsDialog: @Composable (() -> Unit), val onDismiss: () -> Unit, ) From 16a3bf88d3e66ba0e33a49dbe5097c23882ed4b3 Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 24 Feb 2025 22:32:37 +0100 Subject: [PATCH 20/26] test: [ANDROAPP-6802] ValidationBar violations horizontal pager --- .../ui/component/ValidationRulesErrorDialog.kt | 9 ++++----- .../aggregates/ui/provider/DataSetModalDialogProvider.kt | 3 +++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt index b2ff691429..f42f7aeef0 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -62,17 +63,15 @@ internal fun ValidationRulesErrorDialog(violations: List) { HorizontalPager( state = pagerState, - modifier = Modifier - .fillMaxSize() - .weight(1f) - .clip(Shape.Large), - pageSpacing = Spacing.Spacing4, + pageSpacing = Spacing.Spacing8, + contentPadding = PaddingValues(horizontal = Spacing.Spacing24), ) { page -> val violation = violations[page] LazyColumn( modifier = Modifier .fillMaxSize() + .clip(Shape.Large) .background(SurfaceColor.PrimaryContainer) .padding(Spacing.Spacing16), ) { diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt index f85607b4b4..affbbc065a 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt @@ -1,5 +1,6 @@ package org.dhis2.mobile.aggregates.ui.provider +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -16,6 +17,7 @@ import org.hisp.dhis.mobile.ui.designsystem.component.ButtonBlock import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellDefaults import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellUIState +import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor internal class DataSetModalDialogProvider( @@ -156,6 +158,7 @@ internal class DataSetModalDialogProvider( }", showTopSectionDivider = false, showBottomSectionDivider = false, + contentPadding = PaddingValues(Spacing.Spacing0), ), content = { ValidationRulesErrorDialog(violations) From 93e94559f1dfc680a04ef24d1fdbc8157ed934e3 Mon Sep 17 00:00:00 2001 From: andresmr Date: Tue, 25 Feb 2025 12:26:11 +0100 Subject: [PATCH 21/26] test: [ANDROAPP-6802] UpdateSnackBar --- .../aggregates/ui/DataSetTableScreen.kt | 29 +++++++++++++++---- .../ui/viewModel/DataSetTableViewModel.kt | 10 +++++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt index 2b62e61f49..975a6b3f50 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt @@ -29,17 +29,19 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.Snackbar +import androidx.compose.material3.SnackbarDefaults import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -47,6 +49,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import kotlinx.coroutines.launch import org.dhis2.mobile.aggregates.model.DataSetDetails import org.dhis2.mobile.aggregates.model.DataSetInstanceParameters import org.dhis2.mobile.aggregates.model.DataSetSection @@ -84,6 +87,9 @@ import org.hisp.dhis.mobile.ui.designsystem.component.table.ui.TableSelection import org.hisp.dhis.mobile.ui.designsystem.theme.Radius import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing import org.jetbrains.compose.resources.stringResource +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor +import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor +import org.hisp.dhis.mobile.ui.designsystem.theme.dropShadow import org.koin.compose.viewmodel.koinViewModel import org.koin.core.parameter.parametersOf @@ -121,13 +127,14 @@ fun DataSetInstanceScreen( } } + val scope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } - LaunchedEffect(dataSetScreenState.snackbarMessage) { + scope.launch { dataSetScreenState.snackbarMessage.collect { message -> snackbarHostState.showSnackbar( message = message, - duration = SnackbarDuration.Long, + duration = SnackbarDuration.Short, ) } } @@ -178,10 +185,10 @@ fun DataSetInstanceScreen( ), ) }, - snackbarHost = { SnackbarHost(snackbarHostState) }, + snackbarHost = { CustomSnackbarHost(snackbarHostState) }, floatingActionButton = { AnimatedVisibility( - visible = (dataSetScreenState is DataSetScreenState.Loaded), + visible = ((dataSetScreenState as? DataSetScreenState.Loaded)?.dataSetSectionTable is DataSetSectionTable.Loaded), enter = fadeIn(), exit = fadeOut(), ) { @@ -497,3 +504,15 @@ private fun DataSetTable( bottomContent = {}, ) } + +@Composable +private fun CustomSnackbarHost(hostState: SnackbarHostState) { + SnackbarHost(hostState = hostState) { data -> + Snackbar( + modifier = Modifier.dropShadow(shape = SnackbarDefaults.shape), + snackbarData = data, + containerColor = SurfaceColor.SurfaceBright, + contentColor = TextColor.OnSurface, + ) + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index c6edfd3edf..b0b0511a92 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -395,10 +395,12 @@ internal class DataSetTableViewModel( } SUCCESS -> { + onModalDialogDismissed() onExit(resourceManager.provideSavedAndCompleted()) } ERROR -> { + onModalDialogDismissed() showSnackbar(resourceManager.provideErrorOnCompleteDataset()) } } @@ -406,9 +408,9 @@ internal class DataSetTableViewModel( } private fun onExit(exitMessage: String) { + showSnackbar(exitMessage) viewModelScope.launch { withContext(dispatcher.main()) { - showSnackbar(exitMessage) onClose() } } @@ -424,7 +426,9 @@ internal class DataSetTableViewModel( } } - private suspend fun showSnackbar(message: String) { - _dataSetScreenState.value.sendSnackbarMessage(message) + private fun showSnackbar(message: String) { + viewModelScope.launch { + _dataSetScreenState.value.sendSnackbarMessage(message) + } } } From f0b0fefff8510c491bccfc2b39f9a11b4d20141e Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 26 Feb 2025 12:21:03 +0100 Subject: [PATCH 22/26] test: [ANDROAPP-6802] Implement SnackbarController --- .../aggregates/ui/DataSetTableScreen.kt | 16 +++++++++---- .../aggregates/ui/snackbar/ObserveAsEvents.kt | 23 ++++++++++++++++++ .../ui/snackbar/SnackbarController.kt | 24 +++++++++++++++++++ .../ui/states/DataSetScreenState.kt | 9 ------- .../ui/viewModel/DataSetTableViewModel.kt | 8 ++++++- .../ui/viewModel/DataSetTableViewModelTest.kt | 2 ++ 6 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/snackbar/ObserveAsEvents.kt create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/snackbar/SnackbarController.kt diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt index 975a6b3f50..dfcb66fcdc 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt @@ -60,6 +60,8 @@ import org.dhis2.mobile.aggregates.ui.constants.INPUT_DIALOG_TAG import org.dhis2.mobile.aggregates.ui.constants.SYNC_BUTTON_TAG import org.dhis2.mobile.aggregates.ui.inputs.InputProvider import org.dhis2.mobile.aggregates.ui.component.ValidationBar +import org.dhis2.mobile.aggregates.ui.snackbar.ObserveAsEvents +import org.dhis2.mobile.aggregates.ui.snackbar.SnackbarController import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.dhis2.mobile.aggregates.ui.viewModel.DataSetTableViewModel @@ -127,13 +129,19 @@ fun DataSetInstanceScreen( } } - val scope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + + ObserveAsEvents( + flow = SnackbarController.events, + snackbarHostState, + ) { + event -> + scope.launch { + snackbarHostState.currentSnackbarData?.dismiss() - scope.launch { - dataSetScreenState.snackbarMessage.collect { message -> snackbarHostState.showSnackbar( - message = message, + message = event.message, duration = SnackbarDuration.Short, ) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/snackbar/ObserveAsEvents.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/snackbar/ObserveAsEvents.kt new file mode 100644 index 0000000000..5f5e630880 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/snackbar/ObserveAsEvents.kt @@ -0,0 +1,23 @@ +package org.dhis2.mobile.aggregates.ui.snackbar + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.platform.LocalLifecycleOwner +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext + +@Composable +fun ObserveAsEvents( + flow: Flow, + key1: Any? = null, + key2: Any? = null, + onEvent: (T) -> Unit, +) { + val lifecycleOwner = LocalLifecycleOwner.current + LaunchedEffect(lifecycleOwner.lifecycle, key1, key2, flow) { + withContext(Dispatchers.Main.immediate) { + flow.collect(onEvent) + } + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/snackbar/SnackbarController.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/snackbar/SnackbarController.kt new file mode 100644 index 0000000000..0732af7988 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/snackbar/SnackbarController.kt @@ -0,0 +1,24 @@ +package org.dhis2.mobile.aggregates.ui.snackbar + +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.receiveAsFlow + +data class SnackbarEvent( + val message: String, + val action: SnackbarAction? = null, +) + +data class SnackbarAction( + val name: String, + val action: suspend () -> Unit, +) + +object SnackbarController { + + private val _events = Channel() + val events = _events.receiveAsFlow() + + suspend fun sendEvent(event: SnackbarEvent) { + _events.send(event) + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt index 4eb33264f5..ce0966fb9c 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetScreenState.kt @@ -1,7 +1,5 @@ package org.dhis2.mobile.aggregates.ui.states -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow import org.dhis2.mobile.aggregates.model.DataSetDetails import org.dhis2.mobile.aggregates.model.DataSetRenderingConfig import org.dhis2.mobile.aggregates.model.DataSetSection @@ -9,13 +7,6 @@ import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableModel internal sealed class DataSetScreenState { - private val _snackbarMessage = MutableSharedFlow() - val snackbarMessage: SharedFlow get() = _snackbarMessage - - suspend fun sendSnackbarMessage(message: String) { - _snackbarMessage.emit(message) - } - data class Loaded( val dataSetDetails: DataSetDetails, val dataSetSections: List, diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index b0b0511a92..5dd0baab7c 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -44,6 +44,8 @@ import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator import org.dhis2.mobile.aggregates.ui.inputs.UiAction import org.dhis2.mobile.aggregates.ui.provider.DataSetModalDialogProvider import org.dhis2.mobile.aggregates.ui.provider.ResourceManager +import org.dhis2.mobile.aggregates.ui.snackbar.SnackbarController +import org.dhis2.mobile.aggregates.ui.snackbar.SnackbarEvent import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.dhis2.mobile.aggregates.ui.states.ValidationBarUiState @@ -428,7 +430,11 @@ internal class DataSetTableViewModel( private fun showSnackbar(message: String) { viewModelScope.launch { - _dataSetScreenState.value.sendSnackbarMessage(message) + SnackbarController.sendEvent( + event = SnackbarEvent( + message = message, + ), + ) } } } diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt index 59cd225630..29d42f7b78 100644 --- a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt @@ -282,6 +282,8 @@ internal class DataSetTableViewModelTest : KoinTest { whenever(checkValidationRulesConfiguration()) doReturn NONE // And data set instance is completed whenever(checkCompletionStatus()) doReturn COMPLETED + // And data set is saved + whenever(resourceManager.provideSaved()) doReturn "saved" // When attempt to save viewModel.onSaveClicked() From 4b65e11a3244867157bb863b0111b30e54b9ab9b Mon Sep 17 00:00:00 2001 From: andresmr Date: Wed, 26 Feb 2025 22:11:05 +0100 Subject: [PATCH 23/26] test: [ANDROAPP-6802] Moving resource manager --- .../mobile/aggregates/di/AggregateModule.kt | 3 +-- .../model/mapper/IndicatorsMapToTableModel.kt | 2 +- .../model/mapper/TableGroupToTableModel.kt | 2 +- .../TableModelToTableModelWithTotalRow.kt | 2 +- .../TableModelToTableModelWithUpdatedValue.kt | 2 +- .../aggregates/ui/DataSetTableScreen.kt | 14 +++++-------- .../aggregates/ui/provider/ResourceManager.kt | 2 +- .../ui/viewModel/DataSetTableViewModel.kt | 8 +------ .../ui/viewModel/DataSetTableViewModelTest.kt | 21 +++++++++---------- 9 files changed, 22 insertions(+), 34 deletions(-) diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt index c4ff810839..2187ba6ccc 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/di/AggregateModule.kt @@ -9,9 +9,8 @@ import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators import org.dhis2.mobile.aggregates.domain.GetDataValueData import org.dhis2.mobile.aggregates.domain.GetDataValueInput -import org.dhis2.mobile.aggregates.ui.provider.ResourceManager -import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.domain.RunValidationRules +import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher import org.dhis2.mobile.aggregates.ui.provider.DataSetModalDialogProvider import org.dhis2.mobile.aggregates.ui.provider.ResourceManager diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/IndicatorsMapToTableModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/IndicatorsMapToTableModel.kt index 9889c04dd2..8edc14af87 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/IndicatorsMapToTableModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/IndicatorsMapToTableModel.kt @@ -1,8 +1,8 @@ package org.dhis2.mobile.aggregates.model.mapper import org.dhis2.mobile.aggregates.domain.IndicatorMap -import org.dhis2.mobile.aggregates.domain.ResourceManager import org.dhis2.mobile.aggregates.ui.constants.INDICATOR_TABLE_UID +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.hisp.dhis.mobile.ui.designsystem.component.table.model.RowHeader import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableCell import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableHeader diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableGroupToTableModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableGroupToTableModel.kt index b8b084d2d7..43ad0783d8 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableGroupToTableModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableGroupToTableModel.kt @@ -1,6 +1,5 @@ package org.dhis2.mobile.aggregates.model.mapper -import org.dhis2.mobile.aggregates.domain.ResourceManager import org.dhis2.mobile.aggregates.model.DataSetInstanceSectionData import org.dhis2.mobile.aggregates.model.DataValueData import org.dhis2.mobile.aggregates.model.TableGroup @@ -9,6 +8,7 @@ import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator.totalRow import org.dhis2.mobile.aggregates.ui.inputs.TableId import org.dhis2.mobile.aggregates.ui.inputs.TableIdType +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.hisp.dhis.mobile.ui.designsystem.component.table.model.RowHeader import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableCell import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableHeader diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableModelToTableModelWithTotalRow.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableModelToTableModelWithTotalRow.kt index 08df6fc416..bd48795a43 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableModelToTableModelWithTotalRow.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableModelToTableModelWithTotalRow.kt @@ -1,9 +1,9 @@ package org.dhis2.mobile.aggregates.model.mapper -import org.dhis2.mobile.aggregates.domain.ResourceManager import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator.totalCellId import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator.totalHeaderRowId import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator.totalId +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.hisp.dhis.mobile.ui.designsystem.component.table.model.RowHeader import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableCell import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableModel diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableModelToTableModelWithUpdatedValue.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableModelToTableModelWithUpdatedValue.kt index 8179276400..2a279e909d 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableModelToTableModelWithUpdatedValue.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/model/mapper/TableModelToTableModelWithUpdatedValue.kt @@ -1,7 +1,7 @@ package org.dhis2.mobile.aggregates.model.mapper -import org.dhis2.mobile.aggregates.domain.ResourceManager import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator.totalHeaderRowId +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableModel internal suspend fun TableModel.updateValue( diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt index dfcb66fcdc..f640a1a106 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt @@ -2,12 +2,11 @@ package org.dhis2.mobile.aggregates.ui -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.slideInVertically -import androidx.compose.animation.slideOutVertically import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Box @@ -21,10 +20,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Done import androidx.compose.material.icons.filled.Sync -import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Done -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -55,19 +51,19 @@ import org.dhis2.mobile.aggregates.model.DataSetInstanceParameters import org.dhis2.mobile.aggregates.model.DataSetSection import org.dhis2.mobile.aggregates.resources.Res import org.dhis2.mobile.aggregates.resources.action_done +import org.dhis2.mobile.aggregates.ui.component.ValidationBar import org.dhis2.mobile.aggregates.ui.constants.INPUT_DIALOG_DONE_TAG import org.dhis2.mobile.aggregates.ui.constants.INPUT_DIALOG_TAG import org.dhis2.mobile.aggregates.ui.constants.SYNC_BUTTON_TAG import org.dhis2.mobile.aggregates.ui.inputs.InputProvider -import org.dhis2.mobile.aggregates.ui.component.ValidationBar import org.dhis2.mobile.aggregates.ui.snackbar.ObserveAsEvents import org.dhis2.mobile.aggregates.ui.snackbar.SnackbarController import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.dhis2.mobile.aggregates.ui.viewModel.DataSetTableViewModel +import org.hisp.dhis.mobile.ui.designsystem.component.BottomSheetShell import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle -import org.hisp.dhis.mobile.ui.designsystem.component.BottomSheetShell import org.hisp.dhis.mobile.ui.designsystem.component.FAB import org.hisp.dhis.mobile.ui.designsystem.component.FABStyle import org.hisp.dhis.mobile.ui.designsystem.component.IconButton @@ -88,10 +84,10 @@ import org.hisp.dhis.mobile.ui.designsystem.component.table.ui.DataTable import org.hisp.dhis.mobile.ui.designsystem.component.table.ui.TableSelection import org.hisp.dhis.mobile.ui.designsystem.theme.Radius import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing -import org.jetbrains.compose.resources.stringResource import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor import org.hisp.dhis.mobile.ui.designsystem.theme.dropShadow +import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel import org.koin.core.parameter.parametersOf diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt index 5c1a336c56..d865c0b102 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt @@ -5,7 +5,6 @@ import org.dhis2.mobile.aggregates.resources.complete import org.dhis2.mobile.aggregates.resources.complete_anyway import org.dhis2.mobile.aggregates.resources.dataset_saved_completed import org.dhis2.mobile.aggregates.resources.default_column_label -import org.dhis2.mobile.aggregates.resources.total_header_label import org.dhis2.mobile.aggregates.resources.error import org.dhis2.mobile.aggregates.resources.error_on_complete_dataset import org.dhis2.mobile.aggregates.resources.errors @@ -18,6 +17,7 @@ import org.dhis2.mobile.aggregates.resources.ok import org.dhis2.mobile.aggregates.resources.review import org.dhis2.mobile.aggregates.resources.run_validation_rules import org.dhis2.mobile.aggregates.resources.saved +import org.dhis2.mobile.aggregates.resources.total_header_label import org.dhis2.mobile.aggregates.resources.validation_success_title import org.dhis2.mobile.aggregates.resources.yes import org.jetbrains.compose.resources.getString diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt index 5dd0baab7c..97e0786eac 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModel.kt @@ -20,8 +20,8 @@ import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators import org.dhis2.mobile.aggregates.domain.GetDataValueData import org.dhis2.mobile.aggregates.domain.GetDataValueInput -import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.domain.RunValidationRules +import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.COMPLETED import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.NOT_COMPLETED import org.dhis2.mobile.aggregates.model.DataSetMandatoryFieldsStatus.ERROR @@ -33,7 +33,6 @@ import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.MANDATORY import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.NONE import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.OPTIONAL import org.dhis2.mobile.aggregates.model.Violation -import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.mapper.toInputData import org.dhis2.mobile.aggregates.model.mapper.toTableModel import org.dhis2.mobile.aggregates.model.mapper.updateValue @@ -49,11 +48,6 @@ import org.dhis2.mobile.aggregates.ui.snackbar.SnackbarEvent import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.dhis2.mobile.aggregates.ui.states.ValidationBarUiState -import org.hisp.dhis.mobile.ui.designsystem.component.table.model.RowHeader -import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableCell -import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableHeader -import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableHeaderCell -import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableHeaderRow import org.hisp.dhis.mobile.ui.designsystem.component.table.model.TableModel internal class DataSetTableViewModel( diff --git a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt index 29d42f7b78..491d3b5bd6 100644 --- a/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt +++ b/aggregates/src/commonTest/kotlin/org/dhis2/mobile/aggregates/ui/viewModel/DataSetTableViewModelTest.kt @@ -21,11 +21,10 @@ import org.dhis2.mobile.aggregates.domain.GetDataSetSectionData import org.dhis2.mobile.aggregates.domain.GetDataSetSectionIndicators import org.dhis2.mobile.aggregates.domain.GetDataValueData import org.dhis2.mobile.aggregates.domain.GetDataValueInput -import org.dhis2.mobile.aggregates.ui.provider.ResourceManager +import org.dhis2.mobile.aggregates.domain.RunValidationRules import org.dhis2.mobile.aggregates.domain.SetDataValue import org.dhis2.mobile.aggregates.model.CellElement import org.dhis2.mobile.aggregates.model.CellInfo -import org.dhis2.mobile.aggregates.domain.RunValidationRules import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.COMPLETED import org.dhis2.mobile.aggregates.model.DataSetCompletionStatus.NOT_COMPLETED import org.dhis2.mobile.aggregates.model.DataSetDetails @@ -43,11 +42,11 @@ import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.NONE import org.dhis2.mobile.aggregates.model.ValidationRulesConfiguration.OPTIONAL import org.dhis2.mobile.aggregates.model.ValidationRulesResult import org.dhis2.mobile.aggregates.ui.dispatcher.Dispatcher -import org.dhis2.mobile.aggregates.ui.provider.DataSetModalDialogProvider -import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.ui.inputs.CellIdGenerator import org.dhis2.mobile.aggregates.ui.inputs.TableId import org.dhis2.mobile.aggregates.ui.inputs.TableIdType +import org.dhis2.mobile.aggregates.ui.provider.DataSetModalDialogProvider +import org.dhis2.mobile.aggregates.ui.provider.ResourceManager import org.dhis2.mobile.aggregates.ui.states.DataSetModalDialogUIState import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable @@ -82,7 +81,6 @@ internal class DataSetTableViewModelTest : KoinTest { private lateinit var dispatcher: Dispatcher private lateinit var testDispatcher: TestDispatcher - private lateinit var viewModel: DataSetTableViewModel private lateinit var checkValidationRulesConfiguration: CheckValidationRulesConfiguration private lateinit var checkCompletionStatus: CheckCompletionStatus private lateinit var dataSetModalDialogProvider: DataSetModalDialogProvider @@ -115,6 +113,7 @@ internal class DataSetTableViewModelTest : KoinTest { declareMock() { whenever(runBlocking { defaultHeaderLabel() }) doReturn "HeaderLabel" whenever(runBlocking { totalsHeader() }) doReturn "TotalsHeader" + whenever(runBlocking { provideSaved() }) doReturn "saved" } dispatcher = declareMock() checkValidationRulesConfiguration = declareMock() @@ -183,7 +182,7 @@ internal class DataSetTableViewModelTest : KoinTest { whenever(getIndicators(any())).thenReturn(null) viewModel = DataSetTableViewModel( - onCloseCallback = onCloseCallback, + onClose = onCloseCallback, getDataSetInstanceData = get(), getDataSetSectionData = get(), getDataValueData = get(), @@ -191,10 +190,12 @@ internal class DataSetTableViewModelTest : KoinTest { getDataValueInput = get(), setDataValue = get(), resourceManager = get(), + checkValidationRulesConfiguration = get(), + checkCompletionStatus = get(), dispatcher = get(), - get(), - get(), - get(), + datasetModalDialogProvider = get(), + completeDataSet = get(), + runValidationRules = get(), ) } @@ -282,8 +283,6 @@ internal class DataSetTableViewModelTest : KoinTest { whenever(checkValidationRulesConfiguration()) doReturn NONE // And data set instance is completed whenever(checkCompletionStatus()) doReturn COMPLETED - // And data set is saved - whenever(resourceManager.provideSaved()) doReturn "saved" // When attempt to save viewModel.onSaveClicked() From 502c28132501712573129c040ff5dcaf03658b57 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 27 Feb 2025 06:57:30 +0100 Subject: [PATCH 24/26] test: [ANDROAPP-6802] clean up ValidationRulesErrorDialog --- .../ui/ValidationRulesErrorDialogPreview.kt | 39 ------------------- .../component/ValidationRulesErrorDialog.kt | 3 +- 2 files changed, 1 insertion(+), 41 deletions(-) delete mode 100644 aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt diff --git a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt b/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt deleted file mode 100644 index f8b67ea5e5..0000000000 --- a/aggregates/src/androidMain/kotlin/org/dhis2/mobile/aggregates/ui/ValidationRulesErrorDialogPreview.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.dhis2.mobile.aggregates.ui - -import androidx.compose.runtime.Composable -import androidx.compose.ui.tooling.preview.Preview -import org.dhis2.mobile.aggregates.model.Violation -import org.dhis2.mobile.aggregates.ui.component.ValidationBar -import org.dhis2.mobile.aggregates.ui.component.ValidationRulesErrorDialog -import org.dhis2.mobile.aggregates.ui.states.ValidationBarUiState - -@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) -@Composable -fun ValidationRulesErrorDialogPreview() { - ValidationRulesErrorDialog( - listOf( - Violation( - "Please adjust the Chickenpox total to match the combined counts.", - "CPTotal = CPMale + CPFemale", - emptyList(), - ), - Violation( - "description", - "field", - emptyList(), - ), - ), - ) -} - -@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) -@Composable -fun ValidationBarPreview() { - ValidationBar( - ValidationBarUiState( - quantity = 1, - description = "description", - onExpandErrors = {}, - ), - ) -} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt index f42f7aeef0..bc69979bb3 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt @@ -144,8 +144,7 @@ internal fun PagerIndicator( LazyRow( state = indicatorScrollState, modifier = Modifier -// .height(50.dp) - .width(((6 + 16) * 2 + 3 * (10 + 16)).dp), // I'm hard computing it to simplify + .width(120.dp), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { From a39f32db2952a5e02fecd3dd8303a9f8a56f5f29 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 27 Feb 2025 11:38:34 +0100 Subject: [PATCH 25/26] test: [ANDROAPP-6802] Add translations to data to review --- .../src/commonMain/composeResources/values-ar/strings.xml | 1 + .../src/commonMain/composeResources/values-cs/strings.xml | 1 + .../src/commonMain/composeResources/values-es/strings.xml | 1 + .../src/commonMain/composeResources/values-fr/strings.xml | 1 + .../src/commonMain/composeResources/values-id/strings.xml | 1 + .../src/commonMain/composeResources/values-lo/strings.xml | 1 + .../src/commonMain/composeResources/values-nb/strings.xml | 1 + .../src/commonMain/composeResources/values-nl/strings.xml | 1 + .../src/commonMain/composeResources/values-pt/strings.xml | 1 + .../src/commonMain/composeResources/values-uk/strings.xml | 2 ++ .../src/commonMain/composeResources/values-uz/strings.xml | 1 + .../src/commonMain/composeResources/values-vi/strings.xml | 1 + .../commonMain/composeResources/values-zh-rCN/strings.xml | 1 + .../src/commonMain/composeResources/values-zh/strings.xml | 1 + .../src/commonMain/composeResources/values/strings.xml | 2 ++ .../aggregates/ui/component/ValidationRulesErrorDialog.kt | 7 +++++-- 16 files changed, 22 insertions(+), 2 deletions(-) diff --git a/aggregates/src/commonMain/composeResources/values-ar/strings.xml b/aggregates/src/commonMain/composeResources/values-ar/strings.xml index bd604d9872..24684252de 100644 --- a/aggregates/src/commonMain/composeResources/values-ar/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-ar/strings.xml @@ -11,5 +11,6 @@ هل تريد التحقق من جودة البيانات؟ خطأ إكمال على أية حال + بيانات للمراجعة \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-cs/strings.xml b/aggregates/src/commonMain/composeResources/values-cs/strings.xml index 7e345c71b9..e6bce2238c 100644 --- a/aggregates/src/commonMain/composeResources/values-cs/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-cs/strings.xml @@ -12,5 +12,6 @@ Chyba Přesto dokončit Posouzení + Data ke kontrole \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-es/strings.xml b/aggregates/src/commonMain/composeResources/values-es/strings.xml index a2c3f972ca..4fce818805 100644 --- a/aggregates/src/commonMain/composeResources/values-es/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-es/strings.xml @@ -13,5 +13,6 @@ Error Completar de todas formas Revisar + Datos a revisar diff --git a/aggregates/src/commonMain/composeResources/values-fr/strings.xml b/aggregates/src/commonMain/composeResources/values-fr/strings.xml index 1182ef6dde..46b907d7f8 100644 --- a/aggregates/src/commonMain/composeResources/values-fr/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-fr/strings.xml @@ -12,5 +12,6 @@ Erreur Terminer quand même Vérifier + Données à examiner \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-id/strings.xml b/aggregates/src/commonMain/composeResources/values-id/strings.xml index 75c4afd544..18ec631f52 100644 --- a/aggregates/src/commonMain/composeResources/values-id/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-id/strings.xml @@ -11,5 +11,6 @@ Apakah Anda ingin memeriksa kualitas data? Kesalahan Lengkap + Data untuk ditinjau \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-lo/strings.xml b/aggregates/src/commonMain/composeResources/values-lo/strings.xml index 86fb21efb9..d0e3b6cf29 100644 --- a/aggregates/src/commonMain/composeResources/values-lo/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-lo/strings.xml @@ -11,5 +11,6 @@ ທ່ານຕ້ອງການກວດສອບຄຸນນະພາບຂໍ້ມູນນີ້ບໍ? ເກີດການຜິດພາດ ທົບທວນຄືນ + ຂໍ້ມູນທີ່ຕ້ອງທົບທວນ \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nb/strings.xml b/aggregates/src/commonMain/composeResources/values-nb/strings.xml index f17a88d6f6..4af3fd1ce9 100644 --- a/aggregates/src/commonMain/composeResources/values-nb/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-nb/strings.xml @@ -11,5 +11,6 @@ Vil du sjekke datakvaliteten? Feil Fullfør likevel + Data til gjennomgang \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-nl/strings.xml b/aggregates/src/commonMain/composeResources/values-nl/strings.xml index 1a0ed667d7..9397ecf536 100644 --- a/aggregates/src/commonMain/composeResources/values-nl/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-nl/strings.xml @@ -13,5 +13,6 @@ Fout In ieder geval compleet Beoordeling + Gegevens om te bekijken \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-pt/strings.xml b/aggregates/src/commonMain/composeResources/values-pt/strings.xml index 7f32231fcd..d54c3dda2a 100644 --- a/aggregates/src/commonMain/composeResources/values-pt/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-pt/strings.xml @@ -13,5 +13,6 @@ Erro Completar mesmo assim Revisão + Dados para revisar diff --git a/aggregates/src/commonMain/composeResources/values-uk/strings.xml b/aggregates/src/commonMain/composeResources/values-uk/strings.xml index 9bcbd64cc0..6ed6602d92 100644 --- a/aggregates/src/commonMain/composeResources/values-uk/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-uk/strings.xml @@ -13,4 +13,6 @@ Помилка Все одно завершити Перегляд + Дані для перегляду + \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-uz/strings.xml b/aggregates/src/commonMain/composeResources/values-uz/strings.xml index 0bfb09e32d..5b146d2da3 100644 --- a/aggregates/src/commonMain/composeResources/values-uz/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-uz/strings.xml @@ -11,5 +11,6 @@ Маълумотлар сифатини текширишни хохлайсизми? Хатолик Барибир тўлдиринг + Кўриб чиқиладиган маълумотлар \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-vi/strings.xml b/aggregates/src/commonMain/composeResources/values-vi/strings.xml index 26f17cae94..59af1d228f 100644 --- a/aggregates/src/commonMain/composeResources/values-vi/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-vi/strings.xml @@ -13,5 +13,6 @@ Lỗi Hoàn tất luôn Xem xét + Dữ liệu để xem xét \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml b/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml index e44474bbe1..623f563ff2 100644 --- a/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-zh-rCN/strings.xml @@ -10,5 +10,6 @@ 保存并完成! 你要检查数据质量? 任何都要完成 + 复查的数据 \ No newline at end of file diff --git a/aggregates/src/commonMain/composeResources/values-zh/strings.xml b/aggregates/src/commonMain/composeResources/values-zh/strings.xml index 891735444d..e8103bcd6e 100644 --- a/aggregates/src/commonMain/composeResources/values-zh/strings.xml +++ b/aggregates/src/commonMain/composeResources/values-zh/strings.xml @@ -13,5 +13,6 @@ 错误 任何都要完成 审查 + 复查的数据 diff --git a/aggregates/src/commonMain/composeResources/values/strings.xml b/aggregates/src/commonMain/composeResources/values/strings.xml index 468e9d8d7e..d43125fed0 100644 --- a/aggregates/src/commonMain/composeResources/values/strings.xml +++ b/aggregates/src/commonMain/composeResources/values/strings.xml @@ -35,4 +35,6 @@ Errors Complete anyway Review + Data to review + \ No newline at end of file diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt index bc69979bb3..534ead5389 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationRulesErrorDialog.kt @@ -36,11 +36,14 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import org.dhis2.mobile.aggregates.model.Violation +import org.dhis2.mobile.aggregates.resources.Res +import org.dhis2.mobile.aggregates.resources.validation_rules_data_to_review import org.hisp.dhis.mobile.ui.designsystem.component.Tag import org.hisp.dhis.mobile.ui.designsystem.component.TagType import org.hisp.dhis.mobile.ui.designsystem.theme.Shape import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor +import org.jetbrains.compose.resources.stringResource import kotlin.math.max @Composable @@ -96,7 +99,7 @@ internal fun ValidationRulesErrorDialog(violations: List) { } item { Text( - text = "Data to review", + text = stringResource(Res.string.validation_rules_data_to_review), style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold, ) @@ -165,7 +168,7 @@ internal fun PagerIndicator( ) Box( modifier = Modifier - .padding(all = 8.dp) + .padding(all = Spacing.Spacing8) .background(color = color, CircleShape) .size( size, From 94203a446b8516c3733695d945c383ef9aeb1d12 Mon Sep 17 00:00:00 2001 From: andresmr Date: Thu, 27 Feb 2025 12:32:47 +0100 Subject: [PATCH 26/26] test: [ANDROAPP-6802] Remove composables from DataSetModalDialogUIState --- .../aggregates/ui/DataSetTableScreen.kt | 13 +- .../ui/component/ValidationBottomSheet.kt | 188 ++++++++++++++++++ .../ui/provider/DataSetModalDialogProvider.kt | 130 ++---------- .../aggregates/ui/provider/ResourceManager.kt | 22 +- .../ui/states/DataSetModalDialogUIState.kt | 16 +- .../dataSetTable/DataSetInstanceActivity.kt | 3 +- 6 files changed, 218 insertions(+), 154 deletions(-) create mode 100644 aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationBottomSheet.kt diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt index f640a1a106..ed3b839424 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/DataSetTableScreen.kt @@ -52,6 +52,7 @@ import org.dhis2.mobile.aggregates.model.DataSetSection import org.dhis2.mobile.aggregates.resources.Res import org.dhis2.mobile.aggregates.resources.action_done import org.dhis2.mobile.aggregates.ui.component.ValidationBar +import org.dhis2.mobile.aggregates.ui.component.ValidationBottomSheet import org.dhis2.mobile.aggregates.ui.constants.INPUT_DIALOG_DONE_TAG import org.dhis2.mobile.aggregates.ui.constants.INPUT_DIALOG_TAG import org.dhis2.mobile.aggregates.ui.constants.SYNC_BUTTON_TAG @@ -61,7 +62,6 @@ import org.dhis2.mobile.aggregates.ui.snackbar.SnackbarController import org.dhis2.mobile.aggregates.ui.states.DataSetScreenState import org.dhis2.mobile.aggregates.ui.states.DataSetSectionTable import org.dhis2.mobile.aggregates.ui.viewModel.DataSetTableViewModel -import org.hisp.dhis.mobile.ui.designsystem.component.BottomSheetShell import org.hisp.dhis.mobile.ui.designsystem.component.Button import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle import org.hisp.dhis.mobile.ui.designsystem.component.FAB @@ -131,8 +131,7 @@ fun DataSetInstanceScreen( ObserveAsEvents( flow = SnackbarController.events, snackbarHostState, - ) { - event -> + ) { event -> scope.launch { snackbarHostState.currentSnackbarData?.dismiss() @@ -352,13 +351,7 @@ fun DataSetInstanceScreen( } (dataSetScreenState as? DataSetScreenState.Loaded)?.modalDialog?.let { dataSetUIState -> - BottomSheetShell( - uiState = dataSetUIState.contentDialogUIState, - content = dataSetUIState.content, - buttonBlock = dataSetUIState.buttonsDialog, - onDismiss = dataSetUIState.onDismiss, - icon = dataSetUIState.icon, - ) + ValidationBottomSheet(dataSetUIState = dataSetUIState) } } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationBottomSheet.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationBottomSheet.kt new file mode 100644 index 0000000000..aa25f60c77 --- /dev/null +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/component/ValidationBottomSheet.kt @@ -0,0 +1,188 @@ +package org.dhis2.mobile.aggregates.ui.component + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ErrorOutline +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import org.dhis2.mobile.aggregates.model.Violation +import org.dhis2.mobile.aggregates.resources.Res +import org.dhis2.mobile.aggregates.resources.complete +import org.dhis2.mobile.aggregates.resources.complete_anyway +import org.dhis2.mobile.aggregates.resources.no +import org.dhis2.mobile.aggregates.resources.not_now +import org.dhis2.mobile.aggregates.resources.ok +import org.dhis2.mobile.aggregates.resources.review +import org.dhis2.mobile.aggregates.resources.yes +import org.dhis2.mobile.aggregates.ui.states.DataSetModalDialogUIState +import org.dhis2.mobile.aggregates.ui.states.DataSetModalType +import org.dhis2.mobile.aggregates.ui.states.DataSetModalType.COMPLETION +import org.dhis2.mobile.aggregates.ui.states.DataSetModalType.MANDATORY_FIELDS +import org.dhis2.mobile.aggregates.ui.states.DataSetModalType.VALIDATION_RULES +import org.dhis2.mobile.aggregates.ui.states.DataSetModalType.VALIDATION_RULES_ERROR +import org.hisp.dhis.mobile.ui.designsystem.component.BottomSheetShell +import org.hisp.dhis.mobile.ui.designsystem.component.Button +import org.hisp.dhis.mobile.ui.designsystem.component.ButtonBlock +import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle +import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellDefaults +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor +import org.jetbrains.compose.resources.stringResource + +@Composable +internal fun ValidationBottomSheet(dataSetUIState: DataSetModalDialogUIState) { + BottomSheetShell( + uiState = dataSetUIState.contentDialogUIState, + content = { + provideContent( + type = dataSetUIState.type, + violations = dataSetUIState.violations, + ) + }, + buttonBlock = { + provideButtonBlock( + type = dataSetUIState.type, + onPrimaryButtonClick = dataSetUIState.onPrimaryButtonClick, + onSecondaryButtonClick = dataSetUIState.onSecondaryButtonClick, + ) + }, + onDismiss = dataSetUIState.onDismiss, + icon = { + provideIcon(dataSetUIState.type) + }, + ) +} + +@Composable +private fun provideIcon(type: DataSetModalType) { + when (type) { + VALIDATION_RULES_ERROR -> { + Icon( + imageVector = Icons.Outlined.ErrorOutline, + contentDescription = null, + tint = SurfaceColor.Error, + ) + } + + else -> { + // No icon + } + } +} + +@Composable +private fun provideContent( + type: DataSetModalType, + violations: List?, +) { + when (type) { + VALIDATION_RULES_ERROR -> { + violations?.let { + ValidationRulesErrorDialog( + violations = it, + ) + } + } + + else -> { + // No content + } + } +} + +@Composable +private fun provideButtonBlock( + type: DataSetModalType, + onPrimaryButtonClick: () -> Unit, + onSecondaryButtonClick: () -> Unit, +) { + when (type) { + COMPLETION -> { + ButtonBlock( + modifier = Modifier.padding( + BottomSheetShellDefaults.buttonBlockPaddings(), + ), + primaryButton = { + Button( + style = ButtonStyle.OUTLINED, + text = stringResource(Res.string.not_now), + onClick = onPrimaryButtonClick, + modifier = Modifier.fillMaxWidth(), + ) + }, + secondaryButton = { + Button( + style = ButtonStyle.FILLED, + text = stringResource(Res.string.complete), + modifier = Modifier.fillMaxWidth(), + onClick = onSecondaryButtonClick, + ) + }, + ) + } + + MANDATORY_FIELDS -> { + ButtonBlock( + modifier = Modifier.padding( + BottomSheetShellDefaults.buttonBlockPaddings(), + ), + primaryButton = { + Button( + style = ButtonStyle.FILLED, + text = stringResource(Res.string.ok), + modifier = Modifier.fillMaxWidth(), + onClick = onPrimaryButtonClick, + ) + }, + ) + } + + VALIDATION_RULES -> { + ButtonBlock( + modifier = Modifier.padding( + BottomSheetShellDefaults.buttonBlockPaddings(), + ), + primaryButton = { + Button( + style = ButtonStyle.OUTLINED, + text = stringResource(Res.string.no), + onClick = onPrimaryButtonClick, + modifier = Modifier.fillMaxWidth(), + ) + }, + secondaryButton = { + Button( + style = ButtonStyle.FILLED, + text = stringResource(Res.string.yes), + modifier = Modifier.fillMaxWidth(), + onClick = onSecondaryButtonClick, + ) + }, + ) + } + + VALIDATION_RULES_ERROR -> { + ButtonBlock( + modifier = Modifier.padding( + BottomSheetShellDefaults.buttonBlockPaddings(), + ), + primaryButton = { + Button( + style = ButtonStyle.TEXT, + text = stringResource(Res.string.complete_anyway), + onClick = onPrimaryButtonClick, + ) + }, + secondaryButton = { + Button( + style = ButtonStyle.FILLED, + text = stringResource(Res.string.review), + modifier = Modifier.fillMaxWidth(), + onClick = onSecondaryButtonClick, + ) + }, + ) + } + } +} diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt index affbbc065a..d11f149630 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/DataSetModalDialogProvider.kt @@ -1,24 +1,12 @@ package org.dhis2.mobile.aggregates.ui.provider import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Error -import androidx.compose.material.icons.outlined.ErrorOutline -import androidx.compose.material3.Icon -import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import org.dhis2.mobile.aggregates.model.Violation -import org.dhis2.mobile.aggregates.ui.component.ValidationRulesErrorDialog import org.dhis2.mobile.aggregates.ui.states.DataSetModalDialogUIState -import org.hisp.dhis.mobile.ui.designsystem.component.Button -import org.hisp.dhis.mobile.ui.designsystem.component.ButtonBlock -import org.hisp.dhis.mobile.ui.designsystem.component.ButtonStyle -import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellDefaults +import org.dhis2.mobile.aggregates.ui.states.DataSetModalType import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellUIState import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing -import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor internal class DataSetModalDialogProvider( val resourceManager: ResourceManager, @@ -29,9 +17,6 @@ internal class DataSetModalDialogProvider( onNotNow: () -> Unit, onComplete: () -> Unit, ): DataSetModalDialogUIState { - val notNowText = resourceManager.provideNotNow() - val completeText = resourceManager.provideComplete() - return DataSetModalDialogUIState( contentDialogUIState = BottomSheetShellUIState( title = resourceManager.provideCompletionDialogTitle(), @@ -39,30 +24,10 @@ internal class DataSetModalDialogProvider( showBottomSectionDivider = false, headerTextAlignment = TextAlign.Start, ), - buttonsDialog = { - ButtonBlock( - modifier = Modifier.padding( - BottomSheetShellDefaults.buttonBlockPaddings(), - ), - primaryButton = { - Button( - style = ButtonStyle.OUTLINED, - text = notNowText, - onClick = onNotNow, - modifier = Modifier.fillMaxWidth(), - ) - }, - secondaryButton = { - Button( - style = ButtonStyle.FILLED, - text = completeText, - modifier = Modifier.fillMaxWidth(), - onClick = onComplete, - ) - }, - ) - }, onDismiss = onDismiss, + onPrimaryButtonClick = onNotNow, + onSecondaryButtonClick = onComplete, + type = DataSetModalType.COMPLETION, ) } @@ -71,8 +36,6 @@ internal class DataSetModalDialogProvider( onDismiss: () -> Unit, onAccept: () -> Unit, ): DataSetModalDialogUIState { - val acceptText = resourceManager.provideOK() - return DataSetModalDialogUIState( contentDialogUIState = BottomSheetShellUIState( title = resourceManager.provideSaved(), @@ -80,22 +43,9 @@ internal class DataSetModalDialogProvider( showBottomSectionDivider = false, headerTextAlignment = TextAlign.Start, ), - buttonsDialog = { - ButtonBlock( - modifier = Modifier.padding( - BottomSheetShellDefaults.buttonBlockPaddings(), - ), - primaryButton = { - Button( - style = ButtonStyle.FILLED, - text = acceptText, - modifier = Modifier.fillMaxWidth(), - onClick = onAccept, - ) - }, - ) - }, onDismiss = onDismiss, + onPrimaryButtonClick = onAccept, + type = DataSetModalType.MANDATORY_FIELDS, ) } @@ -104,9 +54,6 @@ internal class DataSetModalDialogProvider( onDeny: () -> Unit, onAccept: () -> Unit, ): DataSetModalDialogUIState { - val denyText = resourceManager.provideNo() - val acceptText = resourceManager.provideYes() - return DataSetModalDialogUIState( contentDialogUIState = BottomSheetShellUIState( title = resourceManager.provideSaved(), @@ -114,30 +61,10 @@ internal class DataSetModalDialogProvider( showBottomSectionDivider = false, headerTextAlignment = TextAlign.Start, ), - buttonsDialog = { - ButtonBlock( - modifier = Modifier.padding( - BottomSheetShellDefaults.buttonBlockPaddings(), - ), - primaryButton = { - Button( - style = ButtonStyle.OUTLINED, - text = denyText, - onClick = onDeny, - modifier = Modifier.fillMaxWidth(), - ) - }, - secondaryButton = { - Button( - style = ButtonStyle.FILLED, - text = acceptText, - modifier = Modifier.fillMaxWidth(), - onClick = onAccept, - ) - }, - ) - }, onDismiss = onDismiss, + onPrimaryButtonClick = onDeny, + onSecondaryButtonClick = onAccept, + type = DataSetModalType.VALIDATION_RULES, ) } @@ -146,9 +73,6 @@ internal class DataSetModalDialogProvider( onMarkAsComplete: () -> Unit, violations: List, ): DataSetModalDialogUIState { - val completeAnywayText = resourceManager.provideCompleteAnyway() - val reviewText = resourceManager.provideReview() - return DataSetModalDialogUIState( contentDialogUIState = BottomSheetShellUIState( title = "${violations.size} ${ @@ -160,39 +84,11 @@ internal class DataSetModalDialogProvider( showBottomSectionDivider = false, contentPadding = PaddingValues(Spacing.Spacing0), ), - content = { - ValidationRulesErrorDialog(violations) - }, - icon = { - Icon( - imageVector = Icons.Outlined.ErrorOutline, - contentDescription = null, - tint = SurfaceColor.Error, - ) - }, - buttonsDialog = { - ButtonBlock( - modifier = Modifier.padding( - BottomSheetShellDefaults.buttonBlockPaddings(), - ), - primaryButton = { - Button( - style = ButtonStyle.TEXT, - text = completeAnywayText, - onClick = onMarkAsComplete, - ) - }, - secondaryButton = { - Button( - style = ButtonStyle.FILLED, - text = reviewText, - modifier = Modifier.fillMaxWidth(), - onClick = onDismiss, - ) - }, - ) - }, onDismiss = onDismiss, + onPrimaryButtonClick = onMarkAsComplete, + onSecondaryButtonClick = onDismiss, + type = DataSetModalType.VALIDATION_RULES_ERROR, + violations = violations, ) } } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt index d865c0b102..d5ef40ad8b 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/provider/ResourceManager.kt @@ -1,8 +1,6 @@ package org.dhis2.mobile.aggregates.ui.provider import org.dhis2.mobile.aggregates.resources.Res -import org.dhis2.mobile.aggregates.resources.complete -import org.dhis2.mobile.aggregates.resources.complete_anyway import org.dhis2.mobile.aggregates.resources.dataset_saved_completed import org.dhis2.mobile.aggregates.resources.default_column_label import org.dhis2.mobile.aggregates.resources.error @@ -11,29 +9,21 @@ import org.dhis2.mobile.aggregates.resources.errors import org.dhis2.mobile.aggregates.resources.field_mandatory import org.dhis2.mobile.aggregates.resources.field_required import org.dhis2.mobile.aggregates.resources.mark_dataset_complete -import org.dhis2.mobile.aggregates.resources.no -import org.dhis2.mobile.aggregates.resources.not_now -import org.dhis2.mobile.aggregates.resources.ok -import org.dhis2.mobile.aggregates.resources.review import org.dhis2.mobile.aggregates.resources.run_validation_rules import org.dhis2.mobile.aggregates.resources.saved import org.dhis2.mobile.aggregates.resources.total_header_label import org.dhis2.mobile.aggregates.resources.validation_success_title -import org.dhis2.mobile.aggregates.resources.yes import org.jetbrains.compose.resources.getString internal class ResourceManager { suspend fun defaultHeaderLabel() = getString(Res.string.default_column_label) + suspend fun totalsHeader() = getString(Res.string.total_header_label) suspend fun provideCompletionDialogTitle() = getString(Res.string.validation_success_title) suspend fun provideCompletionDialogDescription() = getString(Res.string.mark_dataset_complete) - suspend fun provideNotNow() = getString(Res.string.not_now) - - suspend fun provideComplete() = getString(Res.string.complete) - suspend fun provideSaved() = getString(Res.string.saved) suspend fun provideMandatoryFieldsMessage() = @@ -42,18 +32,12 @@ internal class ResourceManager { suspend fun provideMandatoryFieldsCombinationMessage() = getString(Res.string.field_required) - suspend fun provideOK() = getString(Res.string.ok) - suspend fun provideSavedAndCompleted() = getString(Res.string.dataset_saved_completed) suspend fun provideErrorOnCompleteDataset() = getString(Res.string.error_on_complete_dataset) suspend fun provideAskRunValidations() = getString(Res.string.run_validation_rules) - suspend fun provideNo() = getString(Res.string.no) - - suspend fun provideYes() = getString(Res.string.yes) - suspend fun provideValidationErrorDescription(errors: Int): String { return if (errors == 1) { getString(Res.string.error) @@ -61,8 +45,4 @@ internal class ResourceManager { getString(Res.string.errors) } } - - suspend fun provideCompleteAnyway() = getString(Res.string.complete_anyway) - - suspend fun provideReview() = getString(Res.string.review) } diff --git a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt index d660c56250..74bd720a8d 100644 --- a/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt +++ b/aggregates/src/commonMain/kotlin/org/dhis2/mobile/aggregates/ui/states/DataSetModalDialogUIState.kt @@ -1,12 +1,20 @@ package org.dhis2.mobile.aggregates.ui.states -import androidx.compose.runtime.Composable +import org.dhis2.mobile.aggregates.model.Violation import org.hisp.dhis.mobile.ui.designsystem.component.state.BottomSheetShellUIState internal data class DataSetModalDialogUIState( val contentDialogUIState: BottomSheetShellUIState, - val content: @Composable (() -> Unit)? = null, - val icon: @Composable (() -> Unit)? = null, - val buttonsDialog: @Composable (() -> Unit), val onDismiss: () -> Unit, + val onPrimaryButtonClick: () -> Unit, + val onSecondaryButtonClick: () -> Unit = {}, + val type: DataSetModalType, + val violations: List? = null, ) + +internal enum class DataSetModalType { + COMPLETION, + MANDATORY_FIELDS, + VALIDATION_RULES, + VALIDATION_RULES_ERROR, +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/DataSetInstanceActivity.kt b/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/DataSetInstanceActivity.kt index 57d1b52fa7..8669a02416 100644 --- a/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/DataSetInstanceActivity.kt +++ b/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/DataSetInstanceActivity.kt @@ -2,7 +2,6 @@ package org.dhis2.usescases.datasets.dataSetTable import android.os.Bundle import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass @@ -18,7 +17,7 @@ class DataSetInstanceActivity : ActivityGlobalAbstract() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - enableEdgeToEdge() +// enableEdgeToEdge() setContent { DHIS2Theme { val useTwoPane = when (calculateWindowSizeClass(this).widthSizeClass) {