diff --git a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardMobileActivityTest.kt b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardMobileActivityTest.kt index d0df3188aa..157dc52e83 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardMobileActivityTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardMobileActivityTest.kt @@ -8,6 +8,8 @@ import dhis2.org.analytics.charts.Charts import io.reactivex.Observable import org.dhis2.R import org.dhis2.android.rtsm.utils.NetworkUtils +import org.dhis2.commons.data.ProgramConfigurationRepository +import org.dhis2.commons.featureconfig.data.FeatureConfigRepository import org.dhis2.commons.filters.FilterManager import org.dhis2.commons.prefs.PreferenceProvider import org.dhis2.commons.resources.MetadataIconProvider @@ -50,6 +52,8 @@ class TeiDashboardMobileActivityTest { private val charts: Charts = mock() private val teiAttributesProvider: TeiAttributesProvider = mock() private val preferences: PreferenceProvider = mock() + private val programConfigurationRepository: ProgramConfigurationRepository = mock() + private val featureConfigRepository: FeatureConfigRepository = mock() private val metadataIconProvider: MetadataIconProvider = mock { on { invoke(any()) }doReturn MetadataIconData.defaultIcon() @@ -120,10 +124,10 @@ class TeiDashboardMobileActivityTest { ENROLLMENT_UID, teiAttributesProvider, preferences, - metadataIconProvider + metadataIconProvider, + programConfigurationRepository, + featureConfigRepository, ) - - } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardProgramModel.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardProgramModel.kt index 6da5e08409..4b650ec10d 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardProgramModel.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardProgramModel.kt @@ -39,6 +39,7 @@ data class DashboardEnrollmentModel( override val teiHeader: String?, override val avatarPath: String?, override val ownerOrgUnit: OrganisationUnit?, + val quickActions: List, ) : DashboardModel( trackedEntityInstance, trackedEntityAttributeValues, diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImpl.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImpl.kt index 6673a21ffc..25d31a6194 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImpl.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImpl.kt @@ -7,7 +7,10 @@ import io.reactivex.Observable import io.reactivex.Single import io.reactivex.functions.Function import org.dhis2.bindings.profilePicturePath +import org.dhis2.commons.data.ProgramConfigurationRepository import org.dhis2.commons.data.tuples.Pair +import org.dhis2.commons.featureconfig.data.FeatureConfigRepository +import org.dhis2.commons.featureconfig.model.Feature import org.dhis2.commons.prefs.Preference import org.dhis2.commons.prefs.PreferenceProvider import org.dhis2.commons.resources.MetadataIconProvider @@ -47,6 +50,8 @@ class DashboardRepositoryImpl( private val teiAttributesProvider: TeiAttributesProvider, private val preferenceProvider: PreferenceProvider, private val metadataIconProvider: MetadataIconProvider, + private val programConfigurationRepository: ProgramConfigurationRepository, + private val featureConfigRepository: FeatureConfigRepository, ) : DashboardRepository { override fun getTeiHeader(): String? { return d2.trackedEntityModule().trackedEntitySearch() @@ -413,10 +418,30 @@ class DashboardRepositoryImpl( getTeiHeader(), getTeiProfilePath(), getOwnerOrgUnit(teiUid), + getQuickActions(programUid), ) } } + private fun getQuickActions(programUid: String): List { + return if (featureConfigRepository.isFeatureEnable(Feature.QUICK_ACTIONS)) { + listOf( + "MARK_FOLLOW_UP", + "TRANSFER", + "COMPLETE_ENROLLMENT", + "CANCEL_ENROLLMENT", + "MORE_ENROLLMENTS", + ) + } else { + /*programConfigurationRepository + .getConfigurationByProgram(programUid) + ?.quickActions() + ?.map { it.actionId() } + ?:*/ + emptyList() + } + } + override fun getTeiActivePrograms( teiUid: String, showOnlyActive: Boolean, diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardViewModel.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardViewModel.kt index b9c1faea4a..dd2167a9fb 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardViewModel.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardViewModel.kt @@ -223,7 +223,6 @@ class DashboardViewModel( ).blockingFirst() if (result == StatusChangeResultCode.CHANGED) { - _showStatusBar.value = status _syncNeeded.value = true _state.value = State.TO_UPDATE fetchDashboardModel() diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardModule.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardModule.kt index c8f1ba6401..1ae85f303d 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardModule.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardModule.kt @@ -3,7 +3,9 @@ package org.dhis2.usescases.teiDashboard import dagger.Module import dagger.Provides import dhis2.org.analytics.charts.Charts +import org.dhis2.commons.data.ProgramConfigurationRepository import org.dhis2.commons.di.dagger.PerActivity +import org.dhis2.commons.featureconfig.data.FeatureConfigRepository import org.dhis2.commons.matomo.MatomoAnalyticsController import org.dhis2.commons.prefs.PreferenceProvider import org.dhis2.commons.resources.EventResourcesProvider @@ -89,6 +91,8 @@ class TeiDashboardModule( preferenceProvider: PreferenceProvider, teiAttributesProvider: TeiAttributesProvider, metadataIconProvider: MetadataIconProvider, + programConfigurationRepository: ProgramConfigurationRepository, + featureConfigRepository: FeatureConfigRepository, ): DashboardRepository { return DashboardRepositoryImpl( d2, @@ -99,6 +103,8 @@ class TeiDashboardModule( teiAttributesProvider, preferenceProvider, metadataIconProvider, + programConfigurationRepository, + featureConfigRepository, ) } @@ -145,4 +151,11 @@ class TeiDashboardModule( resourcesManager, ) } + + @Provides + fun provideProgramConfigurationRepository( + d2: D2, + ): ProgramConfigurationRepository { + return ProgramConfigurationRepository(d2) + } } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/teidata/TEIDataFragment.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/teidata/TEIDataFragment.kt index c92295d179..4ab70ea4dd 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/teidata/TEIDataFragment.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/teidata/TEIDataFragment.kt @@ -64,8 +64,10 @@ import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingDialog.Comp import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingDialog.Companion.SCHEDULING_EVENT_SKIPPED import org.dhis2.usescases.teiDashboard.ui.TeiDetailDashboard import org.dhis2.usescases.teiDashboard.ui.mapper.InfoBarMapper +import org.dhis2.usescases.teiDashboard.ui.mapper.QuickActionsMapper import org.dhis2.usescases.teiDashboard.ui.mapper.TeiDashboardCardMapper import org.dhis2.usescases.teiDashboard.ui.model.InfoBarType +import org.dhis2.usescases.teiDashboard.ui.model.QuickActionType import org.dhis2.usescases.teiDashboard.ui.model.TimelineEventsHeaderModel import org.dhis2.utils.extension.setIcon import org.dhis2.utils.granularsync.SyncStatusDialog @@ -91,6 +93,9 @@ class TEIDataFragment : FragmentGlobalAbstract(), TEIDataContracts.View { @Inject lateinit var infoBarMapper: InfoBarMapper + @Inject + lateinit var quickActionsMapper: QuickActionsMapper + @Inject lateinit var contractHandler: TeiDataContractHandler @@ -303,10 +308,35 @@ class TEIDataFragment : FragmentGlobalAbstract(), TEIDataContracts.View { timelineOnEventCreationOptionSelected = { presenter.onAddNewEventOptionSelected(it, null) }, + quickActions = (dashboardModel as? DashboardEnrollmentModel)?.let { + quickActionsMapper.map(it) { quickActionType -> + onQuickAction(quickActionType) + } + } ?: emptyList(), ) } } } + private fun onQuickAction(quickActionType: QuickActionType) { + val teiDashboardActivity = activity as TeiDashboardMobileActivity + when (quickActionType) { + QuickActionType.MARK_FOLLOW_UP -> dashboardViewModel.onFollowUp() + QuickActionType.TRANSFER -> programUid?.let { + teiDashboardActivity.showOrgUnitSelector(it) + } + QuickActionType.COMPLETE_ENROLLMENT -> dashboardViewModel.updateEnrollmentStatus( + EnrollmentStatus.COMPLETED, + ) + QuickActionType.CANCEL_ENROLLMENT -> dashboardViewModel.updateEnrollmentStatus( + EnrollmentStatus.CANCELLED, + ) + QuickActionType.REOPEN_ENROLLMENT -> dashboardViewModel.updateEnrollmentStatus( + EnrollmentStatus.ACTIVE, + ) + QuickActionType.MORE_ENROLLMENTS -> + teiDashboardActivity.goToEnrollmentList() + } + } private fun showLegacyCard(dashboardModel: DashboardTEIModel?) { binding.noEnrollmentSelected = true diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/teidata/TEIDataModule.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/teidata/TEIDataModule.kt index fa34d5a7d6..6a6caf7398 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/teidata/TEIDataModule.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/teidata/TEIDataModule.kt @@ -28,6 +28,7 @@ import org.dhis2.usescases.teiDashboard.DashboardRepository import org.dhis2.usescases.teiDashboard.dashboardfragments.teidata.teievents.ui.mapper.TEIEventCardMapper import org.dhis2.usescases.teiDashboard.domain.GetNewEventCreationTypeOptions import org.dhis2.usescases.teiDashboard.ui.mapper.InfoBarMapper +import org.dhis2.usescases.teiDashboard.ui.mapper.QuickActionsMapper import org.dhis2.usescases.teiDashboard.ui.mapper.TeiDashboardCardMapper import org.dhis2.utils.analytics.AnalyticsHelper import org.hisp.dhis.android.core.D2 @@ -132,13 +133,6 @@ class TEIDataModule( return GetNewEventCreationTypeOptions(programConfigurationRepository) } - @Provides - fun provideProgramConfigurationRepository( - d2: D2, - ): ProgramConfigurationRepository { - return ProgramConfigurationRepository(d2) - } - @Provides fun provideEventCreationsOptionsMapper( resourceManager: ResourceManager, @@ -160,6 +154,13 @@ class TEIDataModule( return InfoBarMapper(resourceManager) } + @Provides + fun provideQuickActionMapper( + resourceManager: ResourceManager, + ): QuickActionsMapper { + return QuickActionsMapper(programUid, resourceManager) + } + @Provides fun provideContractHandler() = TeiDataContractHandler(registry) diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/TeiDetailDashboard.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/TeiDetailDashboard.kt index 5c56cbea73..cad1f10ec7 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/TeiDetailDashboard.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/TeiDetailDashboard.kt @@ -1,18 +1,24 @@ package org.dhis2.usescases.teiDashboard.ui +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp import org.dhis2.commons.data.EventCreationType import org.dhis2.usescases.teiDashboard.ui.model.InfoBarUiModel +import org.dhis2.usescases.teiDashboard.ui.model.QuickActionUiModel import org.dhis2.usescases.teiDashboard.ui.model.TeiCardUiModel import org.dhis2.usescases.teiDashboard.ui.model.TimelineEventsHeaderModel +import org.hisp.dhis.mobile.ui.designsystem.component.AssistChip import org.hisp.dhis.mobile.ui.designsystem.component.CardDetail import org.hisp.dhis.mobile.ui.designsystem.component.InfoBar import org.hisp.dhis.mobile.ui.designsystem.component.InfoBarData @@ -27,6 +33,7 @@ fun TeiDetailDashboard( timelineEventHeaderModel: TimelineEventsHeaderModel, isGrouped: Boolean = true, timelineOnEventCreationOptionSelected: (EventCreationType) -> Unit, + quickActions: List, ) { Column( modifier = Modifier @@ -99,6 +106,21 @@ fun TeiDetailDashboard( ) } + if (quickActions.isNotEmpty()) { + LazyRow( + horizontalArrangement = Arrangement.spacedBy(Spacing.Spacing8), + contentPadding = PaddingValues(horizontal = Spacing.Spacing16), + ) { + items(quickActions) { + AssistChip( + label = it.label, + icon = it.icon, + onClick = it.onActionClick, + ) + } + } + } + if (!isGrouped) { Spacer(modifier = Modifier.size(Spacing.Spacing16)) TimelineEventsHeader( diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/mapper/QuickActionsMapper.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/mapper/QuickActionsMapper.kt new file mode 100644 index 0000000000..623a4313b2 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/mapper/QuickActionsMapper.kt @@ -0,0 +1,112 @@ +package org.dhis2.usescases.teiDashboard.ui.mapper + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.Assignment +import androidx.compose.material.icons.outlined.Block +import androidx.compose.material.icons.outlined.CheckCircle +import androidx.compose.material.icons.outlined.Flag +import androidx.compose.material.icons.outlined.LockReset +import androidx.compose.material.icons.outlined.MoveDown +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import org.dhis2.R +import org.dhis2.commons.resources.ResourceManager +import org.dhis2.usescases.teiDashboard.DashboardEnrollmentModel +import org.dhis2.usescases.teiDashboard.ui.model.QuickActionType +import org.dhis2.usescases.teiDashboard.ui.model.QuickActionUiModel +import org.hisp.dhis.android.core.enrollment.EnrollmentStatus +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor + +class QuickActionsMapper( + val programUid: String?, + val resourceManager: ResourceManager, +) { + fun map( + dashboardEnrollmentModel: DashboardEnrollmentModel, + onActionClick: (QuickActionType) -> Unit, + ): List { + return dashboardEnrollmentModel.quickActions + .mapNotNull { QuickActionType.valueOf(it).withEnrollmentStatus(dashboardEnrollmentModel) } + .map { quickActionType -> + QuickActionUiModel( + label = getText(quickActionType, programUid), + icon = getIcon(quickActionType), + onActionClick = { onActionClick(quickActionType) }, + ) + } + } + + private fun QuickActionType.withEnrollmentStatus( + dashboardEnrollmentModel: DashboardEnrollmentModel, + ): QuickActionType? { + return when (this) { + QuickActionType.MARK_FOLLOW_UP -> + if (dashboardEnrollmentModel.currentEnrollment.followUp() == true) { + null + } else { + this + } + QuickActionType.COMPLETE_ENROLLMENT -> + if (dashboardEnrollmentModel.currentEnrollment.status() == EnrollmentStatus.COMPLETED) { + QuickActionType.REOPEN_ENROLLMENT + } else { + this + } + QuickActionType.CANCEL_ENROLLMENT -> + if (dashboardEnrollmentModel.currentEnrollment.status() == EnrollmentStatus.CANCELLED) { + QuickActionType.REOPEN_ENROLLMENT + } else { + this + } + else -> this + } + } + + private fun getText( + quickActionType: QuickActionType, + programUid: String?, + ): String { + return when (quickActionType) { + QuickActionType.MARK_FOLLOW_UP -> resourceManager.getString(R.string.mark_follow_up) + QuickActionType.TRANSFER -> resourceManager.getString(R.string.transfer) + QuickActionType.COMPLETE_ENROLLMENT -> resourceManager.formatWithEnrollmentLabel( + programUid, + R.string.complete_enrollment_label, + 1, + ) + QuickActionType.CANCEL_ENROLLMENT -> + resourceManager.formatWithEnrollmentLabel( + programUid, + R.string.deactivate_enrollment_label, + 1, + ) + QuickActionType.REOPEN_ENROLLMENT -> resourceManager.formatWithEnrollmentLabel( + programUid, + R.string.reopen_enrollment_label, + 1, + ) + QuickActionType.MORE_ENROLLMENTS -> resourceManager.getString(R.string.more_enrollments) + } + } + + private fun getIcon(quickActionType: QuickActionType) = @Composable { + val iconResource = when (quickActionType) { + QuickActionType.MARK_FOLLOW_UP -> Icons.Outlined.Flag + QuickActionType.TRANSFER -> Icons.Outlined.MoveDown + QuickActionType.COMPLETE_ENROLLMENT -> Icons.Outlined.CheckCircle + QuickActionType.CANCEL_ENROLLMENT -> Icons.Outlined.Block + QuickActionType.REOPEN_ENROLLMENT -> Icons.Outlined.LockReset + QuickActionType.MORE_ENROLLMENTS -> Icons.AutoMirrored.Outlined.Assignment + } + Icon( + imageVector = iconResource, + contentDescription = null, + tint = if (quickActionType == QuickActionType.REOPEN_ENROLLMENT) { + SurfaceColor.Warning + } else { + MaterialTheme.colorScheme.onSurface + }, + ) + } +} diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/model/TeiDashboardUiModels.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/model/TeiDashboardUiModels.kt index d11be394b4..2ff6912658 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/model/TeiDashboardUiModels.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/ui/model/TeiDashboardUiModels.kt @@ -27,8 +27,23 @@ data class InfoBarUiModel( val showInfoBar: Boolean = false, ) +data class QuickActionUiModel( + val label: String, + val icon: @Composable () -> Unit, + val onActionClick: () -> Unit, +) + enum class InfoBarType { SYNC, FOLLOW_UP, ENROLLMENT_STATUS, } + +enum class QuickActionType { + MARK_FOLLOW_UP, + TRANSFER, + COMPLETE_ENROLLMENT, + CANCEL_ENROLLMENT, + REOPEN_ENROLLMENT, + MORE_ENROLLMENTS, +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8b22d468ff..c87429edbd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -993,4 +993,7 @@ Successfully transferred %1$s cancelled Due date updated + Complete %s + Deactivate %s + Re-open %s diff --git a/app/src/test/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImplTest.kt b/app/src/test/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImplTest.kt index 3b0be2b479..d7177f772f 100644 --- a/app/src/test/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImplTest.kt +++ b/app/src/test/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImplTest.kt @@ -2,6 +2,8 @@ package org.dhis2.usescases.teiDashboard import dhis2.org.analytics.charts.Charts import io.reactivex.Single +import org.dhis2.commons.data.ProgramConfigurationRepository +import org.dhis2.commons.featureconfig.data.FeatureConfigRepository import org.dhis2.commons.prefs.PreferenceProvider import org.dhis2.commons.resources.MetadataIconProvider import org.dhis2.commons.resources.ResourceManager @@ -36,6 +38,8 @@ class DashboardRepositoryImplTest { private val teiAttributesProvider: TeiAttributesProvider = mock() private val preferences: PreferenceProvider = mock() private val metadataIconProvider: MetadataIconProvider = mock() + private val programConfigurationRepository: ProgramConfigurationRepository = mock() + private val featureConfigRepository: FeatureConfigRepository = mock() @Before fun setUp() { @@ -48,6 +52,8 @@ class DashboardRepositoryImplTest { teiAttributesProvider, preferences, metadataIconProvider, + programConfigurationRepository, + featureConfigRepository, ) } diff --git a/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/InfoBarMapperTest.kt b/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/InfoBarMapperTest.kt index 8dc6b56fc0..d88bdccf09 100644 --- a/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/InfoBarMapperTest.kt +++ b/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/InfoBarMapperTest.kt @@ -115,6 +115,7 @@ class InfoBarMapperTest { null, null, null, + emptyList(), ) return model diff --git a/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/QuickActionsMapperTest.kt b/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/QuickActionsMapperTest.kt new file mode 100644 index 0000000000..e45ce63a9e --- /dev/null +++ b/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/QuickActionsMapperTest.kt @@ -0,0 +1,118 @@ +package org.dhis2.usescases.teiDashboard.ui.mapper + +import org.dhis2.R +import org.dhis2.commons.resources.ResourceManager +import org.dhis2.usescases.teiDashboard.DashboardEnrollmentModel +import org.dhis2.usescases.teiDashboard.ui.model.QuickActionType +import org.hisp.dhis.android.core.enrollment.Enrollment +import org.hisp.dhis.android.core.enrollment.EnrollmentStatus +import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstance +import org.junit.Before +import org.junit.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +class QuickActionsMapperTest { + + private val resourceManager: ResourceManager = mock() + private lateinit var mapper: QuickActionsMapper + + @Before + fun setUp() { + mapper = QuickActionsMapper("programUid", resourceManager) + whenever(resourceManager.getString(R.string.more_enrollments)) doReturn "More enrollments" + whenever(resourceManager.getString(R.string.mark_follow_up)) doReturn "Mark follow up" + whenever(resourceManager.getString(R.string.transfer)) doReturn "Transfer" + whenever( + resourceManager.formatWithEnrollmentLabel(mapper.programUid, R.string.complete_enrollment_label, 1), + ) doReturn "Complete enrollment" + whenever( + resourceManager.formatWithEnrollmentLabel(mapper.programUid, R.string.reopen_enrollment_label, 1), + ) doReturn "Re-open enrollment" + whenever( + resourceManager.formatWithEnrollmentLabel(mapper.programUid, R.string.deactivate_enrollment_label, 1), + ) doReturn "Deactivate enrollment" + } + + @Test + fun shouldMapAllItem() { + val dashboardEnrollmentModel = fakeEnrollmentModel( + status = EnrollmentStatus.ACTIVE, + quickActions = listOf( + QuickActionType.MARK_FOLLOW_UP.name, + QuickActionType.TRANSFER.name, + QuickActionType.COMPLETE_ENROLLMENT.name, + QuickActionType.CANCEL_ENROLLMENT.name, + QuickActionType.MORE_ENROLLMENTS.name, + ), + ) + val quickActions = mapper.map(dashboardEnrollmentModel) {} + assert(quickActions.size == 5) + } + + @Test + fun shouldMapMoreEnrollments() { + val dashboardEnrollmentModel = fakeEnrollmentModel( + status = EnrollmentStatus.ACTIVE, + quickActions = listOf(QuickActionType.MORE_ENROLLMENTS.name), + ) + val quickActions = mapper.map(dashboardEnrollmentModel) {} + assert(quickActions.first().label == "More enrollments") + } + + @Test + fun shouldNotMapMarkFollowUpWhenEnrollmentFollowUpIsTrue() { + val dashboardEnrollmentModel = fakeEnrollmentModel( + status = EnrollmentStatus.ACTIVE, + followup = true, + quickActions = listOf(QuickActionType.MARK_FOLLOW_UP.name), + ) + val quickActions = mapper.map(dashboardEnrollmentModel) {} + assert(quickActions.isEmpty()) + } + + @Test + fun shouldShouldShowReopenWhenEnrollmentIsCompleted() { + val dashboardEnrollmentModel = fakeEnrollmentModel( + status = EnrollmentStatus.COMPLETED, + quickActions = listOf( + QuickActionType.COMPLETE_ENROLLMENT.name, + QuickActionType.CANCEL_ENROLLMENT.name, + ), + ) + val quickActions = mapper.map(dashboardEnrollmentModel) {} + assert(quickActions.map { it.label } == listOf("Re-open enrollment", "Deactivate enrollment")) + } + + private fun fakeEnrollmentModel( + status: EnrollmentStatus, + followup: Boolean = false, + quickActions: List = emptyList(), + ): DashboardEnrollmentModel { + val enrollment = Enrollment.builder() + .uid("EnrollmentUid") + .status(status) + .followUp(followup) + .program("Program1Uid") + .build() + val tei = TrackedEntityInstance.builder() + .uid("TEIUid") + .organisationUnit("OrgUnit") + .build() + val model = DashboardEnrollmentModel( + enrollment, + emptyList(), + tei, + listOf(), + emptyList(), + emptyList(), + emptyList(), + null, + null, + null, + quickActions, + ) + return model + } +} diff --git a/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/TEIDetailMapperTest.kt b/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/TEIDetailMapperTest.kt index 548d7c429d..257accf788 100644 --- a/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/TEIDetailMapperTest.kt +++ b/app/src/test/java/org/dhis2/usescases/teiDashboard/ui/mapper/TEIDetailMapperTest.kt @@ -85,6 +85,7 @@ class TEIDetailMapperTest { "header", "avatarFilepath", ownerOrgUnit, + emptyList(), ) return model diff --git a/commons/src/main/java/org/dhis2/commons/featureconfig/data/FeatureConfigRepositoryImpl.kt b/commons/src/main/java/org/dhis2/commons/featureconfig/data/FeatureConfigRepositoryImpl.kt index 8b0ed15655..46d4eac8dc 100644 --- a/commons/src/main/java/org/dhis2/commons/featureconfig/data/FeatureConfigRepositoryImpl.kt +++ b/commons/src/main/java/org/dhis2/commons/featureconfig/data/FeatureConfigRepositoryImpl.kt @@ -61,6 +61,7 @@ class FeatureConfigRepositoryImpl @Inject constructor( Feature.AUTO_LOGOUT -> null Feature.RESPONSIVE_HOME -> FeatureOptions.ResponsiveHome(totalItems = getResponsiveHomeTotalItems()) Feature.COMPOSE_AGGREGATES_SCREEN -> null + Feature.QUICK_ACTIONS -> null } } diff --git a/commons/src/main/java/org/dhis2/commons/featureconfig/model/Feature.kt b/commons/src/main/java/org/dhis2/commons/featureconfig/model/Feature.kt index 68e4ae43ea..70f96e0013 100644 --- a/commons/src/main/java/org/dhis2/commons/featureconfig/model/Feature.kt +++ b/commons/src/main/java/org/dhis2/commons/featureconfig/model/Feature.kt @@ -4,4 +4,5 @@ enum class Feature(val description: String) { AUTO_LOGOUT("automatic log out"), RESPONSIVE_HOME("responsive home"), COMPOSE_AGGREGATES_SCREEN("compose aggregates screen"), + QUICK_ACTIONS("Quick action on TEI dashboard"), }