Skip to content

feat : Migrate Datatable to cmp #2382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 13, 2025
Merged

Conversation

itsPronay
Copy link
Contributor

@itsPronay itsPronay commented May 23, 2025

Fixes - https://mifosforge.jira.com/browse/MIFOSAC-419
Screenshot_2025-06-10-20-47-16-592_org.mifos.client.demo.debug.jpg

Didn't create a Jira ticket, click here to create new.

Please Add Screenshots If there are any UI changes.

Before After

Please make sure these boxes are checked before submitting your pull request - thanks!

  • Run the static analysis check ./gradlew check or ci-prepush.sh to make sure you didn't break anything

  • If you have multiple commits please combine them into one commit by squashing them.

@itsPronay itsPronay marked this pull request as draft May 23, 2025 16:29
@itsPronay itsPronay marked this pull request as ready for review June 10, 2025 15:02
@niyajali
Copy link
Collaborator

@itsPronay resolve merge conflicts

@itsPronay
Copy link
Contributor Author

@niyajali done

@niyajali
Copy link
Collaborator

@HekmatullahAmin @kapmaurya @Aditya3815 Despite repeated requests, why haven't you reviewed the PRs?

@niyajali
Copy link
Collaborator

@itsPronay ask one more team member to approve the PR

@HekmatullahAmin
Copy link
Member

@itsPronay i will review it

alias(libs.plugins.mifos.android.library.compose)
alias(libs.plugins.mifos.android.library.jacoco)
alias(libs.plugins.mifos.cmp.feature)
alias(libs.plugins.mifos.kmp.koin)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mifos.kmp.koin is already added as plugin in CMPFeatureConventionPlugin and KMPLibraryConventionPlugin

} else {
_dataTableUiState.value = DataTableUiState.ShowDataTables(response)
fun loadDataTable(tableName: String?) {
viewModelScope.launch {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Both the refresh and loadDataTable independently call viewModelScope.launch, leading to Redundant coroutine launches.
  • make the loadDataTable suspend and remove viewModelScope.launch

).show()
onBackPressed()
MifosAlertDialog(
dialogTitle = "Success",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a completed action you should use for it snackbar.
When to use which:

Use an AlertDialog only if:

  • You want the user to acknowledge something critical.
  • There's a decision to be made (Yes/No, Confirm/Cancel).
  • It’s a destructive or risky operation before it's confirmed.

Use Snackbar if:

  • Users don’t need to confirm a successful deletion just be informed.
  • A Snackbar allows the user to continue using the app without interruption.
  • Android Material guidelines encourage using snackbars for lightweight feedback.

_dataTableListUiState.value =
DataTableListUiState.ShowMessage(R.string.feature_data_table_loan_creation_success)
viewModelScope.launch {
repository.createLoansAccount(loansPayload)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could please look into these types of function these doesn't need to be flowable. They are one shot api calls like creating, deleting, editing .... these need to be taken care of

This comment was marked as outdated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw your response below, fixing it accordingly

override fun onNext(loans: Loan?) {
_dataTableListUiState.value =
DataTableListUiState.ShowMessage(R.string.feature_data_table_loan_creation_success)
viewModelScope.launch {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Toast.LENGTH_SHORT,
).show()
onSuccess()
MifosAlertDialog(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here use Snackbar instead of AlertDialog

payload: Map<String, String>,
) {
viewModelScope.launch {
addDataTableEntryUseCase.invoke(table, entityId, payload)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • addDataTableEntry() is a single network call it doesn't emit multiple values or benefit from reactive chaining.
  • Using Flow adds boilerplate: mapping to DataState, managing emissions, and collecting with lifecycle scope.
  • For one-shot results, suspend functions with try/catch are simpler, faster, and easier to test.

Below is just an example change it accordingly and handle others like this:

class AddDataTableEntryUseCase(
    private val repository: DataTableRowDialogRepository,
) {
    suspend operator fun invoke(
        table: String,
        entityId: Int,
        payload: Map<String, String>,
    ): DataState<GenericResponse> {
        return try {
            val response = repository.addDataTableEntry(table, entityId, payload)
            DataState.Success(response)
        } catch (e: Exception) {
            DataState.Error(e)
        }
    }
}
fun addDataTableEntry(table: String, entityId: Int, payload: Map<String, String>) {
    _dataTableRowDialogUiState.value = DataTableRowDialogUiState.Loading

    viewModelScope.launch(Dispatchers.IO) {
        val result = addDataTableEntryUseCase(table, entityId, payload)
        _dataTableRowDialogUiState.value = when (result) {
            is DataState.Success -> DataTableRowDialogUiState.DataTableEntrySuccessfully
            is DataState.Error -> DataTableRowDialogUiState.Error(
                R.string.feature_data_table_failed_to_add_data_table
            )
            else -> DataTableRowDialogUiState.Initial
        }
    }
}

@@ -35,6 +36,9 @@ interface DataTableService {
@Path("entityId") entityId: Int,
): JsonArray

@GET("datatables")
fun getDatatables(@Query("apptable") apptable: String? = null): Flow<List<GetDataTablesResponse>>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already in core -> network -> apis -> DataTablesApi.kt is there any reason you created here otherwise delete it

Copy link
Contributor Author

@itsPronay itsPronay Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HekmatullahAmin I am getting ResponseConverter error when i use baseApiManager instead of mBaseApiManager & that's why I used mBaseApiManager instead of baseApiManager

Could not figure out exactly why this is happening. Any idea!?!

Copy link
Contributor

@biplab1 biplab1 Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@itsPronay I think adding a FlowConverterFactory in FineractClient.kt's ktofitBuilder (like we did it in NetworkModule.kt) might resolve the ResponseConverter error for now:

val ktorfitBuilder = Ktorfit.Builder()
                .httpClient(ktorClient)
                .baseUrl(baseURL)
                .converterFactories(FlowConverterFactory())
                .build()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@itsPronay if you see that error it is completely obvious because the converter class is not added when doing DI.
@biplab1 brother i thought in previous PR's you took care of it and add it why it is missing or you forgot.

if (result.isEmpty()) {
_dataTableUiState.value = DataTableUiState.ShowEmptyDataTables
} else {
_dataTableUiState.value = DataTableUiState.ShowDataTables(result)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sometimes when you see a part of code is duplicated have a look if there is a way to avaoid duplication of code:

is DataState.Success -> {
    val result = dataState.data
    _dataTableUiState.value = if (result.isEmpty()) {
        DataTableUiState.ShowEmptyDataTables
    } else {
        DataTableUiState.ShowDataTables(result)
    }
}

}

fun deleteDataTableEntry(table: String, entity: Int, rowId: Int) =
viewModelScope.launch(Dispatchers.IO) {
viewModelScope.launch {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Why deleting a table should be flowable this is wrong properly handle it in DeleteDataTableEntryUseCase
  • also have a look at GetDataTableInfoUseCase and fix it accordingly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right now, there are few issues with the domain module as @biplab1 mentioned, there is a separate jira ticket as well for that. we will be working on it after this migration and fixing those issues in domain

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@itsPronay i just looked at the repository in so many use cases although the api call is suspend the change it to flow and then call it as a one time api call these need to be take care of. If there is a seperate ticket no issue.

// fun getIdOfSelectedItem(key: String): Int {
// return spinnerValueIdMap[key]!!
// }
// }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed this block of code is commented out — could you share why it's been left that way? Was it for debugging, future use, or is it safe to remove?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kept it for future reference since there are so many todos in datatable module. once we take care of todos these will be deleted

@HekmatullahAmin
Copy link
Member

@itsPronay when you are done with the rest of changes that need to be made let me know i will approve.

@itsPronay itsPronay changed the title Draft : Migrate Datatable to cmp draft : Migrate Datatable to cmp Jun 12, 2025
@itsPronay itsPronay changed the title draft : Migrate Datatable to cmp feat : Migrate Datatable to cmp Jun 12, 2025
@itsPronay
Copy link
Contributor Author

@itsPronay when you are done with the rest of changes that need to be made let me know i will approve.

@HekmatullahAmin done, just left out the domain-related changes since we will be working on that in a separate ticket and fixing those accordingly in all viewmodel

@HekmatullahAmin
Copy link
Member

@itsPronay when you are done with the rest of changes that need to be made let me know i will approve.

@HekmatullahAmin done, just left out the domain-related changes since we will be working on that in a separate ticket and fixing those accordingly in all viewmodel

@itsPronay great, just do one thing for those you commented that needed to be done later like from XML to compose please add a TODO so in our studio we can see what things are in the TODO section. It will be easy to chase.

@itsPronay
Copy link
Contributor Author

@HekmatullahAmin There are no todos from my side since I have already migrated the XML to Compose. The existing todos in the DataTable module were already present before.

also I noticed that the old XML code is still there along with those todos, so I kept the old code as well, as we might need it while implementing them

onCreate = {},
)
}

// private fun createForm(table: DataTable?) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@itsPronay by adding TODO I mean for example this code is commented at above add why and when it will be taken care of.
for ex: this is commented because of X and we will fix it after doing Y

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh.okok. I'll add it. thanks for the clarification

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@itsPronay when i say TODO you should type it TODO in capital letter and then it will be higlighted as yellow otherwise it will be the same as the rest of comments.
But no issue next time, have it in your mind.

@HekmatullahAmin
Copy link
Member

@niyajali looks good to me

@niyajali niyajali merged commit db36288 into openMF:kmp-impl Jun 13, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants