Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c91ad3d
added the logic to BreadCrumnbNavBar
ufuomaisaac Oct 6, 2025
1a1ae20
added the logic to BreadCrumnbNavBar
ufuomaisaac Oct 6, 2025
8dfdf65
ClientProfileDetailsScreen fixed
ufuomaisaac Oct 6, 2025
749f570
Client Profile Screen fixed
ufuomaisaac Oct 6, 2025
c907031
problem fixed
ufuomaisaac Oct 6, 2025
586119e
after running the commands
ufuomaisaac Oct 6, 2025
83c574c
Merge branch 'development' into ClientNav
ufuomaisaac Oct 6, 2025
931df6e
Added delay...
ufuomaisaac Oct 8, 2025
503e411
no dialog but the state is being updated in the previous screen
ufuomaisaac Oct 8, 2025
4f00921
Issue resolved
ufuomaisaac Oct 9, 2025
507ab52
Merge branch 'development' into ClientNav
ufuomaisaac Oct 9, 2025
b4fbffe
Created a customized dialog for MifosDialogStates
ufuomaisaac Oct 9, 2025
ca2f132
after rebase
ufuomaisaac Oct 10, 2025
3289eb5
string resource not working
ufuomaisaac Oct 10, 2025
8007673
need to make the constant global within the client module
ufuomaisaac Oct 12, 2025
a8073d8
Added state constant
ufuomaisaac Oct 12, 2025
ee52926
code clean up
ufuomaisaac Oct 13, 2025
24b4601
added more string resources
ufuomaisaac Oct 13, 2025
8e927b9
Merge branch 'development' into ClientNav
ufuomaisaac Nov 3, 2025
a6fadd0
Merge branch 'development' into ClientNav
ufuomaisaac Nov 27, 2025
1fd2c45
Progress; string resource might not be correct
ufuomaisaac Nov 27, 2025
19ee01c
comment the import code for the string resources
ufuomaisaac Nov 27, 2025
262fd40
applied the requested changes
ufuomaisaac Nov 28, 2025
cc4e679
Merge branch 'development' into ClientNav
ufuomaisaac Jan 4, 2026
420501b
implementing repository level approach
ufuomaisaac Jan 5, 2026
fa889aa
Implementing repository level approach
ufuomaisaac Jan 5, 2026
3a6d8b3
code clean-up
ufuomaisaac Jan 5, 2026
312fbe1
code clean-up
ufuomaisaac Jan 5, 2026
6b6161c
resolved navigation when the dialog is dismissed
ufuomaisaac Jan 5, 2026
9735a57
one more...
ufuomaisaac Jan 5, 2026
6c97ec6
code clean
ufuomaisaac Jan 5, 2026
a5e7115
remove unused import
ufuomaisaac Jan 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ package com.mifos.core.data.repository

import com.mifos.room.entities.client.ClientPayloadEntity


interface ClientDetailsEditRepository {

suspend fun updateClient(clientId: Int, clientPayload: ClientPayloadEntity): Int?
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import com.mifos.room.entities.accounts.ClientAccounts
import com.mifos.room.entities.client.ClientEntity
import io.ktor.client.request.forms.MultiPartFormDataContent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow

/**
* Created by Aditya Gupta on 06/08/23.
*/
interface ClientDetailsRepository {

val clientDataUpdated: SharedFlow<Int> // Emits clientId when updated
suspend fun uploadClientImage(clientId: Int, image: MultiPartFormDataContent)

suspend fun deleteClientImage(clientId: Int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.mifos.room.entities.client.ClientPayloadEntity
class ClientDetailsEditRepositoryImpl(
private val dataManagerClient: DataManagerClient,
) : ClientDetailsEditRepository {

override suspend fun updateClient(clientId: Int, clientPayload: ClientPayloadEntity): Int? {
return dataManagerClient.updateClient(clientId, clientPayload)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import com.mifos.room.entities.accounts.ClientAccounts
import com.mifos.room.entities.client.ClientEntity
import io.ktor.client.request.forms.MultiPartFormDataContent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow

/**
* Created by Aditya Gupta on 06/08/23.
Expand All @@ -31,12 +34,21 @@ class ClientDetailsRepositoryImp(
private val dataManagerClient: DataManagerClient,
) : ClientDetailsRepository {

private val _clientDataUpdated = MutableSharedFlow<Int>(
replay = 0,
extraBufferCapacity = 1
)

override val clientDataUpdated: SharedFlow<Int> = _clientDataUpdated.asSharedFlow()

override suspend fun uploadClientImage(clientId: Int, image: MultiPartFormDataContent) {
dataManagerClient.uploadClientImage(clientId, image)
_clientDataUpdated.tryEmit(clientId)
}

override suspend fun deleteClientImage(clientId: Int) {
dataManagerClient.deleteClientImage(clientId)
_clientDataUpdated.tryEmit(clientId)
}

override suspend fun getClientAccounts(clientId: Int): ClientAccounts {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
<string name="core_designsystem_enter_tenant">Enter a tenant</string>
<string name="core_designsystem_cancel">Cancel</string>
<string name="core_designsystem_dialog_action_ok">Save</string>
<string name="core_designsystem_dialog_success">Success</string>
<string name="core_designsystem_dialog_failure">Failure</string>
<string name="core_designsystem_dialog_continue">Continue</string>
<string name="core_designsystem_mifosStatusDialog" translatable="false">MifosStatusDialog</string>

<string name="core_designsystem_app_title">Mifos</string>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ import androidx.compose.foundation.layout.Row
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.layout.width
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
Expand All @@ -32,6 +35,16 @@ import com.mifos.core.designsystem.theme.MifosTypography
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.jetbrains.compose.ui.tooling.preview.PreviewParameter
import org.jetbrains.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Error
import androidx.compose.ui.graphics.Color
import core.designsystem.generated.resources.Res
import core.designsystem.generated.resources.core_designsystem_dialog_continue
import core.designsystem.generated.resources.core_designsystem_dialog_failure
import core.designsystem.generated.resources.core_designsystem_dialog_success
import core.designsystem.generated.resources.core_designsystem_mifosStatusDialog
import org.jetbrains.compose.resources.stringResource

@Composable
fun MifosBasicDialog(
Expand Down Expand Up @@ -197,6 +210,73 @@ fun MifosBasicDialog(
)
}



enum class MifosDialogStatus { SUCCESS, FAILURE }

@Composable
fun MifosStatusDialog(
status: MifosDialogStatus,
message: String,
onDismissRequest: () -> Unit
) {
data class DialogUI(
val title: String,
val icon: androidx.compose.ui.graphics.vector.ImageVector,
val color: Color
)

val dialogUI: DialogUI = when (status) {
MifosDialogStatus.SUCCESS -> DialogUI(
title = stringResource(Res.string.core_designsystem_dialog_success),
icon = Icons.Filled.CheckCircle,
color = Color(0xFF4CAF50)
)
MifosDialogStatus.FAILURE -> DialogUI(
title = stringResource(Res.string.core_designsystem_dialog_failure),
icon = Icons.Filled.Error,
color = Color(0xFFF44336)
)
}

AlertDialog(
onDismissRequest = onDismissRequest,
icon = {
Icon(
imageVector = dialogUI.icon,
contentDescription = dialogUI.title,
tint = dialogUI.color,
modifier = Modifier
.size(84.dp)
.padding(bottom = 4.dp)
)
},
title = {
Text(
text = dialogUI.title,
style = MaterialTheme.typography.titleLarge
)
},
text = {
Text(
text = message,
style = MaterialTheme.typography.bodyMedium
)
},
confirmButton = {
Button(
onClick = onDismissRequest,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
Text(stringResource(Res.string.core_designsystem_dialog_continue))
}
},
modifier = Modifier.testTag(stringResource(Res.string.core_designsystem_mifosStatusDialog))
)
}

@Preview
@Composable
private fun MifosBasicDialog_preview() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,11 @@
<string name="confirm_text">I Confirm</string>
<string name="dismiss_text">Cancel</string>
<string name="dialog_continue">Continue</string>
<string name="update_success_message">Your profile image has been updated successfully!</string>
<string name="profile_update_error_message">We couldn’t update your profile image. Please try again.</string>
<string name="profile_should_refresh">should refresh</string>
<string name="success">Success</string>
<string name="failure">Failure</string>

<!-- Assign Staff Screen -->
<string name="label_assign_staff">Assign Staff</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.mifos.core.ui.util.imageToByteArray
import com.mifos.core.ui.util.toDateString
import com.mifos.feature.client.clientDetailsProfile.components.ClientProfileDetailsActionItem
import com.mifos.room.entities.client.ClientEntity
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.StringResource
Expand Down Expand Up @@ -62,13 +63,24 @@ internal class ClientProfileDetailsViewModel(

init {
getClientAndObserveNetwork()
observeClientUpdates()
}

private fun getClientAndObserveNetwork() {
observeNetwork()
loadClientDetailsAndImage(route.id)
}

private fun observeClientUpdates() {
viewModelScope.launch {
clientDetailsRepo.clientDataUpdated
.filter { updatedClientId -> updatedClientId == route.id }
.collect {
loadClientDetailsAndImage(route.id)
}
}
}

/**
* Fetches both client details and profile image.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.mifos.room.entities.organisation.OfficeEntity
import com.mifos.room.entities.organisation.StaffEntity
import com.mifos.room.entities.templates.clients.ClientsTemplateEntity
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.getString
Expand All @@ -43,10 +44,6 @@ internal class ClientEditDetailsViewModel(
) {
val route = savedStateHandle.toRoute<ClientEditDetailsRoute>()

init {
loadClientDetails(route.id)
}

fun loadClientDetails(clientId: Int = route.id) {
viewModelScope.launch {
getClientDetailsUseCase(clientId).collect { result ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@ import androidclient.feature.client.generated.resources.choose_from_option
import androidclient.feature.client.generated.resources.delete_dialog_message
import androidclient.feature.client.generated.resources.delete_dialog_title
import androidclient.feature.client.generated.resources.delete_photo
import androidclient.feature.client.generated.resources.dialog_continue
import androidclient.feature.client.generated.resources.edit_profile_title
import androidclient.feature.client.generated.resources.feature_client_Image_Upload_Failed
import androidclient.feature.client.generated.resources.feature_client_Image_Upload_Successful
import androidclient.feature.client.generated.resources.feature_client_error
import androidclient.feature.client.generated.resources.from_camera
import androidclient.feature.client.generated.resources.from_gallery
//import androidclient.feature.client.generated.resources.profile_update_error_message
import androidclient.feature.client.generated.resources.remove
import androidclient.feature.client.generated.resources.update_profile_photo_message
//import androidclient.feature.client.generated.resources.update_success_message
import androidclient.feature.client.generated.resources.upload_new_photo
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
Expand All @@ -37,7 +43,9 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
Expand All @@ -48,8 +56,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import com.mifos.core.designsystem.component.BasicDialogState
import com.mifos.core.designsystem.component.MifosBasicDialog
import com.mifos.core.designsystem.component.MifosDialogStatus
import com.mifos.core.designsystem.component.MifosOutlinedButton
import com.mifos.core.designsystem.component.MifosScaffold
import com.mifos.core.designsystem.component.MifosStatusDialog
import com.mifos.core.designsystem.component.MifosTextButton
import com.mifos.core.designsystem.icon.MifosIcons
import com.mifos.core.designsystem.theme.DesignToken
Expand All @@ -76,11 +86,21 @@ internal fun ClientProfileEditScreen(
) {
val state by viewModel.stateFlow.collectAsStateWithLifecycle()

EventsEffect(viewModel.eventFlow) { event ->
when (event) {
ClientProfileEditEvent.NavigateBack -> onNavigateBack()
ClientProfileEditEvent.OnSaveSuccess -> {
onNavigateBack()
var hasShownSuccess by remember { mutableStateOf(false) }

LaunchedEffect(state.dialogState) {
when (state.dialogState) {
is ClientProfileEditState.DialogState.Success -> {
hasShownSuccess = true
}
null -> {
if (hasShownSuccess) {
hasShownSuccess = false
onNavigateBack()
}
}
else -> {
hasShownSuccess = false
}
}
}
Expand Down Expand Up @@ -198,11 +218,18 @@ private fun ClientProfileEditDialogs(
is ClientProfileEditState.DialogState.Loading -> MifosProgressIndicator()

is ClientProfileEditState.DialogState.Error -> {
MifosErrorComponent(
isNetworkConnected = state.networkConnection,
message = state.dialogState.message,
isRetryEnabled = true,
onRetry = onRetry,
MifosStatusDialog(
status = MifosDialogStatus.FAILURE,
message = stringResource(Res.string.feature_client_error),
onDismissRequest = { onAction(ClientProfileEditAction.DismissModalBottomSheet) }
)
Comment on lines 220 to +225
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Error dialog discards the specific error message.

The DialogState.Error contains a message property with the actual error details, but it's not used. The dialog always shows the generic feature_client_error string. Consider displaying the actual error message for better user feedback.

Proposed fix
         is ClientProfileEditState.DialogState.Error -> {
             MifosStatusDialog(
                 status = MifosDialogStatus.FAILURE,
-                message = stringResource(Res.string.feature_client_error),
+                message = state.dialogState.message.ifBlank { 
+                    stringResource(Res.string.feature_client_error) 
+                },
                 onDismissRequest = { onAction(ClientProfileEditAction.DismissModalBottomSheet) }
             )
         }
🤖 Prompt for AI Agents
In
@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientEditProfile/ClientProfileEditScreen.kt
around lines 220-225, The error dialog branch currently ignores the specific
error text in ClientProfileEditState.DialogState.Error; update the
MifosStatusDialog call in the ClientProfileEditState.DialogState.Error branch to
pass the error's message property (e.g., use errorState.message) as the dialog
message, falling back to stringResource(Res.string.feature_client_error) if the
message is null or blank; keep the existing onDismissRequest ->
onAction(ClientProfileEditAction.DismissModalBottomSheet) behavior.

}

is ClientProfileEditState.DialogState.Success -> {
MifosStatusDialog(
status = MifosDialogStatus.SUCCESS,
message = stringResource(Res.string.feature_client_Image_Upload_Successful),
onDismissRequest = { onAction(ClientProfileEditAction.DismissModalBottomSheet) }
)
}

Expand Down
Loading