Skip to content

Commit

Permalink
feat: [ANDROAPP-6229] Display new schedule dialog UI when creating/en…
Browse files Browse the repository at this point in the history
…tering schedule (#3801)

* Clear date value when clearing event report date

* Make yes/no options optional in `SchedulingDialogUi`

* Simplify the program stage check in `GetNewEventCreationTypeOptions`

* Display schedule event dialog when event option of schedule type is selected

* Run code formatting

* Pass program stage to `displayScheduleEvent` function

* Show scheduling dialog when creating timeline event

* Fix broken tests in `TeiDataPresenterTest`

* Change header alignment for schedule dialog bottom sheet

* Rename `SchedulingDialog#newInstance` to `newSchedule`

* Add `LaunchMode` type to `SchedulingDialog`

This help us differentiate between new schedule and enter event launch modes for the `SchedulingDialog`

* Add factory function to launch `SchedulingDialog` in enter event launch mode

* Pass `LaunchMode` to `SchedulingViewModel` as `AssistedInject` param

* Load event due date based on `LaunchMode` in `SchedulingViewModel`

* Handle cancelling events in `SchedulingViewModel`

* Update event due date when date is changed when entering event

* Update `EventDate#currentDate` when event due date is updated

* Use `ConfigureEventReportDate` to load due date for both new schedule and enter event

* Add support for updating due date

* Use loaded program stage of the event

* When primary action button is clicked and launch mode is enter event, then update event status

* Update `SchedulingDialogUi`

* Handle callbacks from `SchedulingViewModel`

* Show `SchedulingDialogUi` when enter event is clicked

* Handle fragment results received from `SchedulingDialog`

Triggering snackbars is pending

* Fix `SchedulingDialogUiTest`

* Set event date when enter event is clicked in `SchedulingDialog`

* Breakdown `scheduleEvent` function into two separate functions

* Extract `ButtonBlock` as separate composable in `SchedulingDialogUi`

* Pass `EventCreationType` to `SchedulingDialog`

* Add snackbar extension to add icon

* Display snackbar when event is cancelled or due date is updated

* Display overdue subtitle in `SchedulingDialog`

* Remove unused test tag from `SchedulingDialogUi`

* Launch old referral flow when referral event is created in event timeline view

* Fix unresolved references in unit tests

* Fix code smells

* Use `DispatcherProvider` in `SchedulingViewModel`

* Update event status when event due date is changed

* Open event forms when skipped events are clicked

* Set event label for the button when `SchedulingDialog` launch mode is `EnterEvent`

* Show event label in the snackbar after the event is cancelled

* Change `SchedulingDialog` title to event label

* Add support for fetching `Due today` string in `toOverdueOrScheduledUiText`

* Return `Next [event]` label when there is no event when fetching label in `ConfigureEventReportDate`

* Set event label for cancel button in `SchedulingDialogUi`

* Fix broken test in `SchedulingDialogUiTest`
  • Loading branch information
msasikanth authored Oct 3, 2024
1 parent 761dc6a commit 4fada06
Show file tree
Hide file tree
Showing 22 changed files with 685 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import kotlinx.coroutines.flow.MutableStateFlow
import org.dhis2.commons.data.EventCreationType
import org.dhis2.composetable.test.TestActivity
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.models.EventCatCombo
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.models.EventCategory
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.models.EventDate
import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingDialog
import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingDialogUi
import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingViewModel
import org.hisp.dhis.android.core.category.CategoryOption
import org.hisp.dhis.android.core.enrollment.Enrollment
import org.hisp.dhis.android.core.program.ProgramStage
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito.mock
Expand All @@ -31,6 +33,7 @@ class SchedulingDialogUiTest {
val composeTestRule = createAndroidComposeRule<TestActivity>()

private val viewModel: SchedulingViewModel = mock()
private val enrollment = Enrollment.builder().uid("enrollmentUid").build()

@Before
fun setUp() {
Expand Down Expand Up @@ -64,10 +67,18 @@ class SchedulingDialogUiTest {
programStages = programStages,
viewModel = viewModel,
orgUnitUid = "orgUnitUid",
launchMode = SchedulingDialog.LaunchMode.NewSchedule(
enrollment = enrollment,
programStages = programStages,
showYesNoOptions = false,
eventCreationType = EventCreationType.SCHEDULE,
)
) {
}
}
composeTestRule.onNodeWithText("Schedule next " + programStages.first().displayName() + "?")

val eventLabel = programStages.first().displayEventLabel() ?: "event"
composeTestRule.onNodeWithText("Schedule next $eventLabel?")
.assertExists()
composeTestRule.onNodeWithText("Program stage").assertDoesNotExist()
composeTestRule.onNodeWithText("Date").assertExists()
Expand All @@ -87,6 +98,12 @@ class SchedulingDialogUiTest {
programStages = programStages,
viewModel = viewModel,
orgUnitUid = "orgUnitUid",
launchMode = SchedulingDialog.LaunchMode.NewSchedule(
enrollment = enrollment,
programStages = programStages,
showYesNoOptions = false,
eventCreationType = EventCreationType.SCHEDULE,
)
) {
}
}
Expand All @@ -106,6 +123,12 @@ class SchedulingDialogUiTest {
programStages = programStages,
viewModel = viewModel,
orgUnitUid = "orgUnitUid",
launchMode = SchedulingDialog.LaunchMode.NewSchedule(
enrollment = enrollment,
programStages = programStages,
showYesNoOptions = true,
eventCreationType = EventCreationType.SCHEDULE,
)
) {
}
}
Expand All @@ -130,6 +153,12 @@ class SchedulingDialogUiTest {
programStages = programStages,
viewModel = viewModel,
orgUnitUid = "orgUnitUid",
launchMode = SchedulingDialog.LaunchMode.NewSchedule(
enrollment = enrollment,
programStages = programStages,
showYesNoOptions = false,
eventCreationType = EventCreationType.SCHEDULE,
)
) {
}
}
Expand All @@ -144,4 +173,30 @@ class SchedulingDialogUiTest {

verify(viewModel).updateStage(programStages[1])
}
}

@Test
fun yesNoFieldsShouldNotBeShownWhenTurnedOff() {
val programStages = listOf(
ProgramStage.builder().uid("stageUidA").displayName("PS A").build(),
ProgramStage.builder().uid("stageUidB").displayName("PS B").build(),
)
whenever(viewModel.programStage).thenReturn(MutableStateFlow(programStages.first()))

composeTestRule.setContent {
SchedulingDialogUi(
programStages = programStages,
viewModel = viewModel,
orgUnitUid = "orgUnitUid",
launchMode = SchedulingDialog.LaunchMode.NewSchedule(
enrollment = enrollment,
programStages = programStages,
showYesNoOptions = false,
eventCreationType = EventCreationType.SCHEDULE,
)
) {
}
}

composeTestRule.onNodeWithTag("YES_NO_OPTIONS").assertDoesNotExist()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ class ConfigureEventReportDate(

private fun getLabel(): String {
val programStage = getProgramStage()
val event = repository.getEvent()

if (event == null) {
return resourceProvider.provideNextEventDate(programStage?.displayEventLabel())
}

return when (creationType) {
SCHEDULE ->
programStage?.dueDateLabel() ?: resourceProvider.provideDueDate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ class EventDetailResourcesProvider(
programUid,
)

fun provideNextEventDate(label: String?): String {
val defaultEventLabel = resourceManager.getString(R.string.event)
return resourceManager.getString(
R.string.next_event,
label ?: defaultEventLabel,
)
}

fun provideEditionStatus(reason: EventNonEditableReason): String {
return when (reason) {
EventNonEditableReason.BLOCKED_BY_COMPLETION ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class TEIDataContracts {
interface View : AbstractActivityContracts.View {
fun viewLifecycleOwner(): LifecycleOwner
fun setEvents(events: List<EventViewModel>)
fun displayScheduleEvent()
fun displayScheduleEvent(programStage: ProgramStage?, showYesNoOptions: Boolean, eventCreationType: EventCreationType)
fun displayEnterEvent(eventUid: String, showYesNoOptions: Boolean, eventCreationType: EventCreationType)
fun showDialogCloseProgram()
fun areEventsCompleted(): Consumer<Single<Boolean>>
fun displayGenerateEvent(eventUid: String)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.setFragmentResultListener
import androidx.lifecycle.LifecycleOwner
Expand Down Expand Up @@ -55,14 +56,18 @@ import org.dhis2.usescases.teiDashboard.dashboardfragments.teidata.teievents.Eve
import org.dhis2.usescases.teiDashboard.dashboardfragments.teidata.teievents.EventCatComboOptionSelector
import org.dhis2.usescases.teiDashboard.dashboardfragments.teidata.teievents.ui.mapper.TEIEventCardMapper
import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingDialog
import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingDialog.Companion.EVENT_LABEL
import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingDialog.Companion.PROGRAM_STAGE_UID
import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingDialog.Companion.SCHEDULING_DIALOG
import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingDialog.Companion.SCHEDULING_DIALOG_RESULT
import org.dhis2.usescases.teiDashboard.dialogs.scheduling.SchedulingDialog.Companion.SCHEDULING_EVENT_DUE_DATE_UPDATED
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.TeiDashboardCardMapper
import org.dhis2.usescases.teiDashboard.ui.model.InfoBarType
import org.dhis2.usescases.teiDashboard.ui.model.TimelineEventsHeaderModel
import org.dhis2.utils.extension.setIcon
import org.dhis2.utils.granularsync.SyncStatusDialog
import org.hisp.dhis.android.core.enrollment.EnrollmentStatus
import org.hisp.dhis.android.core.program.Program
Expand Down Expand Up @@ -175,6 +180,39 @@ class TEIDataFragment : FragmentGlobalAbstract(), TEIDataContracts.View {
)
presenter.fetchEvents()
}

setFragmentResultListener(SCHEDULING_EVENT_SKIPPED) { _, bundle ->
val eventLabel = bundle.getString(EVENT_LABEL) ?: getString(R.string.event)
val snackbar = Snackbar.make(
binding.teiRootView,
requireContext().getString(R.string.event_cancelled, eventLabel.replaceFirstChar { it.uppercaseChar() }),
Snackbar.LENGTH_LONG,
)

snackbar.setIcon(
drawable = ContextCompat.getDrawable(requireContext(), R.drawable.ic_close)!!,
) {
snackbar.dismiss()
}
snackbar.show()
presenter.fetchEvents()
}

setFragmentResultListener(SCHEDULING_EVENT_DUE_DATE_UPDATED) { _, _ ->
val snackbar = Snackbar.make(
binding.teiRootView,
requireContext().getString(R.string.due_date_updated),
Snackbar.LENGTH_LONG,
)

snackbar.setIcon(
drawable = ContextCompat.getDrawable(requireContext(), R.drawable.ic_close)!!,
) {
snackbar.dismiss()
}
snackbar.show()
presenter.fetchEvents()
}
}.root
}

Expand Down Expand Up @@ -396,16 +434,30 @@ class TEIDataFragment : FragmentGlobalAbstract(), TEIDataContracts.View {
}
}

override fun displayScheduleEvent() {
override fun displayScheduleEvent(programStage: ProgramStage?, showYesNoOptions: Boolean, eventCreationType: EventCreationType) {
val model = dashboardViewModel.dashboardModel.value
if (model is DashboardEnrollmentModel) {
SchedulingDialog.newInstance(
SchedulingDialog.newSchedule(
enrollment = model.currentEnrollment,
programStages = presenter.filterAvailableStages(model.programStages),
programStages = if (programStage != null) {
listOf(programStage)
} else {
presenter.filterAvailableStages(model.programStages)
},
showYesNoOptions = showYesNoOptions,
eventCreationType = eventCreationType,
).show(parentFragmentManager, SCHEDULING_DIALOG)
}
}

override fun displayEnterEvent(eventUid: String, showYesNoOptions: Boolean, eventCreationType: EventCreationType) {
SchedulingDialog.enterEvent(
eventUid = eventUid,
showYesNoOptions = showYesNoOptions,
eventCreationType = eventCreationType,
).show(parentFragmentManager, SCHEDULING_DIALOG)
}

override fun showDialogCloseProgram() {
dialog = CustomDialog(
requireContext(),
Expand Down
Loading

0 comments on commit 4fada06

Please sign in to comment.