diff --git a/core/ui/src/commonMain/composeResources/values/strings.xml b/core/ui/src/commonMain/composeResources/values/strings.xml
index 786297c617b..600574b0052 100644
--- a/core/ui/src/commonMain/composeResources/values/strings.xml
+++ b/core/ui/src/commonMain/composeResources/values/strings.xml
@@ -73,4 +73,21 @@
Click Here To View Filled State.
No Item Found
+
+ View Account
+ Edit Note
+ Delete Note
+ Approve Account
+ Make Repayment
+ View Document
+ Upload Again
+ Delete Document
+
+
+ Created By:
+ Date
+ Note
+
+
+
\ No newline at end of file
diff --git a/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosActionsListingCardComponent.kt b/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosActionsListingCardComponent.kt
index 2ee2e1ab3aa..db6b1842bb2 100644
--- a/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosActionsListingCardComponent.kt
+++ b/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosActionsListingCardComponent.kt
@@ -12,17 +12,25 @@ package com.mifos.core.ui.components
import androidclient.core.ui.generated.resources.Res
import androidclient.core.ui.generated.resources.core_ui_account_no
import androidclient.core.ui.generated.resources.core_ui_amount_paid
+import androidclient.core.ui.generated.resources.core_ui_approve_account
import androidclient.core.ui.generated.resources.core_ui_balance
+import androidclient.core.ui.generated.resources.core_ui_delete_document
+import androidclient.core.ui.generated.resources.core_ui_delete_note
import androidclient.core.ui.generated.resources.core_ui_description
import androidclient.core.ui.generated.resources.core_ui_document_id
import androidclient.core.ui.generated.resources.core_ui_document_key
import androidclient.core.ui.generated.resources.core_ui_due
import androidclient.core.ui.generated.resources.core_ui_due_as_of
+import androidclient.core.ui.generated.resources.core_ui_edit_note
import androidclient.core.ui.generated.resources.core_ui_identify_documents
import androidclient.core.ui.generated.resources.core_ui_last_active
import androidclient.core.ui.generated.resources.core_ui_loan_balance
import androidclient.core.ui.generated.resources.core_ui_loan_product
+import androidclient.core.ui.generated.resources.core_ui_make_repayment
import androidclient.core.ui.generated.resources.core_ui_name
+import androidclient.core.ui.generated.resources.core_ui_note_createdBy
+import androidclient.core.ui.generated.resources.core_ui_note_date
+import androidclient.core.ui.generated.resources.core_ui_note_note
import androidclient.core.ui.generated.resources.core_ui_original_loan
import androidclient.core.ui.generated.resources.core_ui_outstanding
import androidclient.core.ui.generated.resources.core_ui_paid
@@ -31,6 +39,9 @@ import androidclient.core.ui.generated.resources.core_ui_status
import androidclient.core.ui.generated.resources.core_ui_total_collateral_value
import androidclient.core.ui.generated.resources.core_ui_total_value
import androidclient.core.ui.generated.resources.core_ui_type
+import androidclient.core.ui.generated.resources.core_ui_upload_again
+import androidclient.core.ui.generated.resources.core_ui_view_account
+import androidclient.core.ui.generated.resources.core_ui_view_document
import androidclient.core.ui.generated.resources.core_ui_waived
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
@@ -49,6 +60,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -61,6 +73,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
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.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalDensity
@@ -70,22 +83,31 @@ import com.mifos.core.designsystem.theme.AppColors
import com.mifos.core.designsystem.theme.DesignToken
import com.mifos.core.designsystem.theme.MifosTypography
import com.mifos.core.designsystem.utils.onClick
+import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
fun MifosActionsListingComponentOutline(
+ bottomRounded: Boolean = false,
+ topRounded: Boolean = false,
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
+ val shape = if (bottomRounded) {
+ DesignToken.shapes.bottomMedium
+ } else if (topRounded) {
+ DesignToken.shapes.topMedium
+ } else {
+ DesignToken.shapes.medium
+ }
+
Box(
- modifier = modifier
- .fillMaxWidth()
- .border(
- width = 1.dp,
- shape = DesignToken.shapes.medium,
- color = MaterialTheme.colorScheme.secondaryContainer,
- ),
+ modifier = modifier.fillMaxWidth().border(
+ width = 1.dp,
+ shape = shape,
+ color = MaterialTheme.colorScheme.secondaryContainer,
+ ),
) {
content()
}
@@ -579,13 +601,123 @@ fun MifosActionsClientFeeListingComponent(
}
}
-enum class Actions(val icon: ImageVector) {
- ViewAccount(MifosIcons.ViewAccount),
- ApproveAccount(MifosIcons.ApproveAccount),
- MakeRepayment(MifosIcons.MakeRepayment),
- ViewDocument(MifosIcons.ViewDocument),
- UploadAgain(MifosIcons.UploadAgain),
- DeleteDocument(MifosIcons.DeleteDocument),
+@Composable
+fun MifosActionsNoteListingComponent(
+ createdBy: String,
+ date: String,
+ notes: String,
+ menuList: List,
+ onExpand: () -> Unit,
+ isExpanded: Boolean,
+ onActionClicked: (Actions) -> Unit,
+) {
+ Column {
+ MifosActionsListingComponentOutline(
+ topRounded = isExpanded,
+ modifier = Modifier
+ .clip(if (isExpanded) DesignToken.shapes.topMedium else DesignToken.shapes.medium)
+ .clickable {
+ onExpand()
+ },
+ ) {
+ Column(
+ modifier = Modifier.padding(DesignToken.padding.large),
+ ) {
+ MifosListingRowItemHeader(
+ text = stringResource(Res.string.core_ui_note_createdBy) + " " + createdBy,
+ keyStyle = MifosTypography.titleSmallEmphasized,
+ )
+
+ Spacer(Modifier.height(DesignToken.padding.large))
+
+ MifosListingColumnItem(
+ key = stringResource(Res.string.core_ui_note_date),
+ value = date,
+ )
+
+ Spacer(Modifier.height(DesignToken.padding.medium))
+
+ MifosListingColumnItem(
+ key = stringResource(Res.string.core_ui_note_note),
+ value = notes,
+ )
+ }
+ }
+
+ AnimatedVisibility(isExpanded) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ shape = DesignToken.shapes.bottomMedium,
+ color = MaterialTheme.colorScheme.surfaceContainer,
+ ) {
+ Column(
+ modifier = Modifier.padding(vertical = DesignToken.spacing.large),
+ verticalArrangement = Arrangement.spacedBy(DesignToken.spacing.medium),
+ ) {
+ menuList.map { menuItem ->
+ Row(
+ modifier = Modifier.fillMaxWidth().padding(
+ horizontal = DesignToken.padding.large,
+ ).clickable {
+ onActionClicked(menuItem)
+ },
+ horizontalArrangement = Arrangement.spacedBy(
+ DesignToken.spacing.medium,
+ ),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ imageVector = menuItem.icon,
+ contentDescription = null,
+ modifier = Modifier.size(DesignToken.sizes.iconMedium),
+ )
+ Text(
+ text = stringResource(menuItem.iconName),
+ style = MaterialTheme.typography.bodyLarge,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+enum class Actions(val icon: ImageVector, val iconName: StringResource) {
+ ViewAccount(MifosIcons.ViewAccount, Res.string.core_ui_view_account),
+ ApproveAccount(MifosIcons.ApproveAccount, Res.string.core_ui_approve_account),
+ MakeRepayment(MifosIcons.MakeRepayment, Res.string.core_ui_make_repayment),
+ ViewDocument(MifosIcons.ViewDocument, Res.string.core_ui_view_document),
+ UploadAgain(MifosIcons.UploadAgain, Res.string.core_ui_upload_again),
+ DeleteDocument(MifosIcons.DeleteDocument, Res.string.core_ui_delete_document),
+ Edit(MifosIcons.Edit, Res.string.core_ui_edit_note),
+ Delete(MifosIcons.Delete, Res.string.core_ui_delete_note),
+}
+
+@Preview
+@Composable
+private fun PreviewMifosActionsNoteListingComponent() {
+ MaterialTheme {
+ MifosActionsNoteListingComponent(
+ createdBy = "John Doe",
+ date = "02 September 2025",
+ notes = "This is a note",
+ menuList = listOf(
+ Actions.Edit,
+ Actions.Delete,
+ ),
+ onExpand = {},
+ isExpanded = true,
+ onActionClicked = { action ->
+ when (action) {
+ Actions.Edit -> println(Actions.ViewAccount.name)
+ Actions.Delete -> println(Actions.ApproveAccount.name)
+ else -> println("Action not Handled")
+ }
+ },
+ )
+ }
}
@Preview
diff --git a/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosListingComponent.kt b/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosListingComponent.kt
index 377fca3ee39..9e956ff497b 100644
--- a/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosListingComponent.kt
+++ b/core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosListingComponent.kt
@@ -108,6 +108,48 @@ fun MifosListingRowItem(
}
}
+@Composable
+fun MifosListingColumnItem(
+ keyContent: @Composable () -> Unit,
+ valueContent: @Composable () -> Unit,
+) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ keyContent()
+ valueContent()
+ }
+}
+
+@Composable
+fun MifosListingColumnItem(
+ key: String,
+ value: String,
+ keyStyle: TextStyle = MifosTypography.labelSmall,
+ keyColor: Color = MaterialTheme.colorScheme.secondary,
+ valueStyle: TextStyle = MifosTypography.bodySmall,
+ valueColor: Color = MaterialTheme.colorScheme.onSurface,
+) {
+ MifosListingColumnItem(
+ keyContent = {
+ Text(
+ text = key,
+ style = keyStyle.copy(color = keyColor),
+ maxLines = 1,
+ overflow = TextOverflow.Clip,
+ )
+ },
+ valueContent = {
+ Text(
+ text = value,
+ style = valueStyle.copy(color = valueColor),
+ overflow = TextOverflow.Clip,
+ maxLines = 1,
+ )
+ },
+ )
+}
+
@Composable
fun MifosListingRowItem(
key: String,
diff --git a/feature/note/src/commonMain/composeResources/values/res.xml b/feature/note/src/commonMain/composeResources/values/res.xml
index 1d3c9cd5ffb..c02b4a4126c 100644
--- a/feature/note/src/commonMain/composeResources/values/res.xml
+++ b/feature/note/src/commonMain/composeResources/values/res.xml
@@ -14,9 +14,6 @@
Click on "+" Button to add an Item
Failed to fetch notes
No Internet
- Created By:
- Date
- Note
Edit Note
Delete Note
Delete
@@ -31,8 +28,6 @@
Add
Update
Back
- Note Updated Successfully
- Note Added Successfully
Confirm
Warning
Discard changes? Unsaved data will be lost.
diff --git a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteRoute.kt b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteRoute.kt
index 88f7de6ae55..79b921f3753 100644
--- a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteRoute.kt
+++ b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteRoute.kt
@@ -24,11 +24,13 @@ data class AddEditNoteRoute(
fun NavGraphBuilder.addEditNoteRoute(
onBackPressed: () -> Unit,
onNavigateWithUpdatedList: (Int, String?) -> Unit,
+ navController: NavController,
) {
composable {
AddEditNoteScreen(
onBackPressed = onBackPressed,
onNavigateWithUpdatedList = onNavigateWithUpdatedList,
+ navController = navController,
)
}
}
diff --git a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteScreen.kt b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteScreen.kt
index 0df203047b7..209069dda36 100644
--- a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteScreen.kt
+++ b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteScreen.kt
@@ -15,7 +15,6 @@ import androidclient.feature.note.generated.resources.feature_note_button_confir
import androidclient.feature.note.generated.resources.feature_note_dialog_warning
import androidclient.feature.note.generated.resources.feature_note_dialog_warning_message
import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
@@ -35,6 +34,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavController
import com.mifos.core.designsystem.component.MifosCircularProgress
import com.mifos.core.designsystem.component.MifosOutlinedButton
import com.mifos.core.designsystem.component.MifosOutlinedTextField
@@ -42,6 +42,7 @@ import com.mifos.core.designsystem.component.MifosScaffold
import com.mifos.core.designsystem.theme.DesignToken
import com.mifos.core.designsystem.theme.MifosTypography
import com.mifos.core.ui.components.MifosAlertDialog
+import com.mifos.core.ui.components.MifosBreadcrumbNavBar
import com.mifos.core.ui.components.MifosErrorComponent
import com.mifos.core.ui.util.EventsEffect
import org.jetbrains.compose.resources.stringResource
@@ -51,6 +52,7 @@ import org.koin.compose.viewmodel.koinViewModel
internal fun AddEditNoteScreen(
onBackPressed: () -> Unit,
onNavigateWithUpdatedList: (Int, String?) -> Unit,
+ navController: NavController,
viewModel: AddEditNoteViewModel = koinViewModel(),
) {
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
@@ -68,6 +70,7 @@ internal fun AddEditNoteScreen(
AddEditNoteScreenScaffold(
state = state,
onAction = remember(viewModel) { { viewModel.trySendAction(it) } },
+ navController = navController,
)
AddEditNoteScreenDialog(
@@ -120,21 +123,28 @@ fun AddEditNoteScreenDialog(
internal fun AddEditNoteScreenScaffold(
onAction: (AddEditNoteAction) -> Unit,
state: AddEditNoteState,
+ navController: NavController,
) {
MifosScaffold(
title = "",
onBackPressed = { onAction(AddEditNoteAction.MisTouchBackDialog) },
) { paddingValues ->
- Box(
+ Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
) {
- AddEditNote(
- state = state,
- onAction = onAction,
- )
+ if (state.dialogState !is AddEditNoteState.DialogState.Loading &&
+ state.dialogState !is AddEditNoteState.DialogState.Error
+ ) {
+ MifosBreadcrumbNavBar(navController)
+
+ AddEditNote(
+ state = state,
+ onAction = onAction,
+ )
+ }
}
}
}
@@ -221,9 +231,6 @@ private fun AddEditNote(
} else {
onAction(AddEditNoteAction.AddNote(state.textFieldNotesPayload))
}
- if (state.isError) {
- onAction(AddEditNoteAction.NavigateBack)
- }
},
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colorScheme.onPrimary,
diff --git a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteViewModel.kt b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteViewModel.kt
index cb4015b7b79..96563f9471d 100644
--- a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteViewModel.kt
+++ b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/addEditNotes/AddEditNoteViewModel.kt
@@ -12,11 +12,9 @@ package com.mifos.feature.note.addEditNotes
import androidclient.feature.note.generated.resources.Res
import androidclient.feature.note.generated.resources.feature_note_Unexpected_error
import androidclient.feature.note.generated.resources.feature_note_add_note
-import androidclient.feature.note.generated.resources.feature_note_add_success
import androidclient.feature.note.generated.resources.feature_note_button_add
import androidclient.feature.note.generated.resources.feature_note_button_update
import androidclient.feature.note.generated.resources.feature_note_edit_note_label
-import androidclient.feature.note.generated.resources.feature_note_edit_success
import androidclient.feature.note.generated.resources.feature_note_update_note
import androidclient.feature.note.generated.resources.feature_note_write_note_label
import androidx.lifecycle.SavedStateHandle
@@ -125,24 +123,24 @@ class AddEditNoteViewModel(
mutableStateFlow.update {
it.copy(
dialogState = AddEditNoteState.DialogState.Error(Res.string.feature_note_Unexpected_error),
- isError = false,
)
}
}
DataState.Loading -> {
- // no need to show loading for this
+ mutableStateFlow.update {
+ it.copy(dialogState = AddEditNoteState.DialogState.Loading)
+ }
}
is DataState.Success -> {
mutableStateFlow.update {
it.copy(
- isError = true,
dialogState = null,
- successMessage = Res.string.feature_note_add_success,
notesPayloadInitialData = state.textFieldNotesPayload.note,
)
}
+ sendEvent(AddEditNoteEvent.NavigateBackWithUpdateList)
}
}
}
@@ -159,22 +157,25 @@ class AddEditNoteViewModel(
mutableStateFlow.update {
it.copy(
dialogState = AddEditNoteState.DialogState.Error(Res.string.feature_note_Unexpected_error),
- isError = false,
)
}
}
+
DataState.Loading -> {
- // no need to show loading for this
+ mutableStateFlow.update {
+ it.copy(dialogState = AddEditNoteState.DialogState.Loading)
+ }
}
+
is DataState.Success -> {
mutableStateFlow.update {
it.copy(
- isError = true,
dialogState = null,
- successMessage = Res.string.feature_note_edit_success,
notesPayloadInitialData = state.textFieldNotesPayload.note,
)
}
+
+ sendEvent(AddEditNoteEvent.NavigateBackWithUpdateList)
}
}
}
@@ -185,6 +186,10 @@ class AddEditNoteViewModel(
override fun handleAction(action: AddEditNoteAction) {
when (action) {
AddEditNoteAction.NavigateBack -> {
+ sendEvent(AddEditNoteEvent.NavigateBack)
+ }
+
+ AddEditNoteAction.NavigateBackWithUpdateList -> {
sendEvent(AddEditNoteEvent.NavigateBackWithUpdateList)
}
@@ -225,7 +230,6 @@ class AddEditNoteViewModel(
mutableStateFlow.update {
it.copy(
dialogState = null,
- showDialog = false,
)
}
}
@@ -235,7 +239,6 @@ class AddEditNoteViewModel(
mutableStateFlow.update {
it.copy(
dialogState = AddEditNoteState.DialogState.MisTouchBack,
- showDialog = true,
)
}
} else {
@@ -250,14 +253,11 @@ data class AddEditNoteState(
val resourceId: Int = -1,
val resourceType: String? = null,
val editEnabled: Boolean = false,
- val successMessage: StringResource? = null,
val addUpdateButton: StringResource = Res.string.feature_note_button_add,
val label: StringResource = Res.string.feature_note_write_note_label,
val title: StringResource = Res.string.feature_note_add_note,
val textFieldNotesPayload: NotesPayload = NotesPayload(null),
val notesPayloadInitialData: String? = null,
- val showDialog: Boolean = false,
- val isError: Boolean = true,
val dialogState: DialogState? = null,
val networkConnection: Boolean = false,
) {
@@ -275,6 +275,7 @@ sealed interface AddEditNoteEvent {
sealed interface AddEditNoteAction {
data object NavigateBack : AddEditNoteAction
+ data object NavigateBackWithUpdateList : AddEditNoteAction
data object OnRetry : AddEditNoteAction
data class AddNote(val notesPayload: NotesPayload) : AddEditNoteAction
data class EditNote(val notesPayload: NotesPayload) : AddEditNoteAction
diff --git a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/navigation/NoteNavigation.kt b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/navigation/NoteNavigation.kt
index 5697ac5a0ef..99127a9f7f8 100644
--- a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/navigation/NoteNavigation.kt
+++ b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/navigation/NoteNavigation.kt
@@ -32,6 +32,7 @@ fun NavGraphBuilder.noteNavGraph(
noteRoute(
onNavigateBack = onBackPressed,
onNavigateAddEditNote = navController::navigateToAddEditNoteScreen,
+ navController = navController,
)
addEditNoteRoute(
@@ -39,6 +40,7 @@ fun NavGraphBuilder.noteNavGraph(
navController.popBackStack()
},
onNavigateWithUpdatedList = navController::navigateToNoteScreenWithUpdatedList,
+ navController = navController,
)
}
}
diff --git a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteRoute.kt b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteRoute.kt
index c88ccedfad9..048e22e15b6 100644
--- a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteRoute.kt
+++ b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteRoute.kt
@@ -23,11 +23,13 @@ data class NoteRoute(
fun NavGraphBuilder.noteRoute(
onNavigateBack: () -> Unit,
onNavigateAddEditNote: (Int, String?, Long?) -> Unit,
+ navController: NavController,
) {
composable {
- NoteScreenScaffold(
+ NoteScreen(
onNavigateBack = onNavigateBack,
onNavigateAddEditNote = onNavigateAddEditNote,
+ navController = navController,
)
}
}
diff --git a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteScreen.kt b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteScreen.kt
index 77d147ead36..484f44425c5 100644
--- a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteScreen.kt
+++ b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteScreen.kt
@@ -11,23 +11,14 @@ package com.mifos.feature.note.notes
import androidclient.feature.note.generated.resources.Res
import androidclient.feature.note.generated.resources.feature_note_add_item
-import androidclient.feature.note.generated.resources.feature_note_createdBy
-import androidclient.feature.note.generated.resources.feature_note_date
import androidclient.feature.note.generated.resources.feature_note_delete
import androidclient.feature.note.generated.resources.feature_note_delete_note
import androidclient.feature.note.generated.resources.feature_note_delete_note_confirmation
-import androidclient.feature.note.generated.resources.feature_note_edit_note
import androidclient.feature.note.generated.resources.feature_note_item
-import androidclient.feature.note.generated.resources.feature_note_no_item_found
-import androidclient.feature.note.generated.resources.feature_note_note
import androidclient.feature.note.generated.resources.feature_note_notes
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
-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
@@ -37,8 +28,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -48,22 +37,25 @@ import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavController
+import androidx.navigation.compose.rememberNavController
import com.mifos.core.common.utils.DateHelper
-import com.mifos.core.designsystem.component.MifosCard
import com.mifos.core.designsystem.component.MifosCircularProgress
import com.mifos.core.designsystem.component.MifosScaffold
import com.mifos.core.designsystem.icon.MifosIcons
import com.mifos.core.designsystem.theme.DesignToken
import com.mifos.core.designsystem.theme.MifosTypography
import com.mifos.core.model.objects.notes.Note
+import com.mifos.core.ui.components.Actions
+import com.mifos.core.ui.components.MifosActionsNoteListingComponent
import com.mifos.core.ui.components.MifosAlertDialog
+import com.mifos.core.ui.components.MifosBreadcrumbNavBar
+import com.mifos.core.ui.components.MifosEmptyCard
import com.mifos.core.ui.components.MifosErrorComponent
import com.mifos.core.ui.util.DevicePreview
import com.mifos.core.ui.util.EventsEffect
@@ -74,9 +66,10 @@ import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
@Composable
-internal fun NoteScreenScaffold(
+internal fun NoteScreen(
onNavigateBack: () -> Unit,
onNavigateAddEditNote: (Int, String?, Long?) -> Unit,
+ navController: NavController,
viewModel: NoteViewModel = koinViewModel(),
) {
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
@@ -84,17 +77,25 @@ internal fun NoteScreenScaffold(
EventsEffect(viewModel.eventFlow) { event ->
when (event) {
NoteEvent.NavigateBack -> onNavigateBack()
- NoteEvent.NavigateAddNote -> onNavigateAddEditNote(state.resourceId, state.resourceType, null)
- NoteEvent.NavigateEditNote -> onNavigateAddEditNote(state.resourceId, state.resourceType, state.expandedNoteId)
+ NoteEvent.NavigateAddNote -> onNavigateAddEditNote(
+ state.resourceId,
+ state.resourceType,
+ null,
+ )
+
+ NoteEvent.NavigateEditNote -> onNavigateAddEditNote(
+ state.resourceId,
+ state.resourceType,
+ state.expandedNoteId,
+ )
}
}
- if (!state.isError && state.notes.isNotEmpty()) {
- NoteScreenScaffold(
- state = state,
- onAction = remember(viewModel) { { viewModel.trySendAction(it) } },
- )
- }
+ NoteScreenScaffold(
+ state = state,
+ onAction = remember(viewModel) { { viewModel.trySendAction(it) } },
+ navController = navController,
+ )
NoteScreenDialog(
state = state,
@@ -121,6 +122,19 @@ private fun NoteScreenDialog(
MifosCircularProgress()
}
+ NoteState.DialogState.ShowDialog -> {
+ MifosAlertDialog(
+ onDismissRequest = {
+ onAction(NoteAction.DismissDialog)
+ },
+ onConfirmation = {
+ onAction(NoteAction.DeleteNote)
+ },
+ confirmationText = stringResource(Res.string.feature_note_delete),
+ dialogTitle = stringResource(Res.string.feature_note_delete_note),
+ dialogText = stringResource(Res.string.feature_note_delete_note_confirmation),
+ )
+ }
null -> Unit
}
}
@@ -128,9 +142,10 @@ private fun NoteScreenDialog(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun NoteScreenScaffold(
- onAction: (NoteAction) -> Unit,
state: NoteState,
+ navController: NavController,
modifier: Modifier = Modifier,
+ onAction: (NoteAction) -> Unit,
) {
val snackBarHostState = remember { SnackbarHostState() }
val pullRefreshState = rememberPullToRefreshState()
@@ -143,19 +158,24 @@ internal fun NoteScreenScaffold(
snackbarHostState = snackBarHostState,
modifier = modifier,
) { paddingValues ->
- Box(
+ Column(
modifier = Modifier.fillMaxSize().padding(paddingValues),
) {
+ MifosBreadcrumbNavBar(navController)
PullToRefreshBox(
state = pullRefreshState,
modifier = Modifier.fillMaxSize(),
isRefreshing = state.isRefreshing,
onRefresh = { onAction(NoteAction.OnRefresh) },
) {
- NoteContent(
- state = state,
- onAction = onAction,
- )
+ if (state.dialogState !is NoteState.DialogState.Loading &&
+ state.dialogState !is NoteState.DialogState.Error
+ ) {
+ NoteContent(
+ state = state,
+ onAction = onAction,
+ )
+ }
}
}
}
@@ -198,235 +218,49 @@ private fun NoteContent(
)
}
- LazyColumn(
- modifier = modifier,
- verticalArrangement = Arrangement.spacedBy(DesignToken.spacing.small),
- horizontalAlignment = Alignment.CenterHorizontally,
- contentPadding = PaddingValues(vertical = DesignToken.spacing.largeIncreased),
- ) {
- if (state.notes.isEmpty()) {
- item {
- MifosCard(
- modifier = Modifier.fillMaxWidth().border(
- width = 1.dp,
- color = MaterialTheme.colorScheme.secondaryContainer,
- shape = DesignToken.shapes.medium,
- ),
- elevation = DesignToken.spacing.none,
- shape = DesignToken.shapes.medium,
- ) {
- Column(
- modifier = Modifier.padding(DesignToken.spacing.large),
- verticalArrangement = Arrangement.spacedBy(DesignToken.spacing.medium),
- ) {
- Text(
- text = stringResource(Res.string.feature_note_no_item_found),
- style = MifosTypography.titleSmallEmphasized,
- color = MaterialTheme.colorScheme.onSurface,
- )
- Text(
- text = stringResource(Res.string.feature_note_add_item),
- style = MaterialTheme.typography.bodySmall,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
- }
- }
- } else {
- items(state.notes.reversed()) { note ->
- NoteItem(
- id = note.id,
- note = note.note,
- createdByUsername = note.createdByUsername,
- createdOn = note.createdOn,
- onAction = onAction,
- state = state,
- )
- }
- }
- }
- }
-}
-
-@Composable
-private fun NoteItem(
- id: Long?,
- note: String?,
- onAction: (NoteAction) -> Unit,
- createdByUsername: String?,
- createdOn: String?,
- state: NoteState,
-) {
- var shape by remember { mutableStateOf(RoundedCornerShape(0.dp)) }
-
- shape = if (state.expandedNoteId == id) {
- DesignToken.shapes.topMedium as RoundedCornerShape
- } else {
- DesignToken.shapes.medium as RoundedCornerShape
- }
+ Spacer(modifier = Modifier.height(DesignToken.spacing.large))
- Column {
- MifosCard(
- modifier = Modifier.fillMaxWidth().border(
- width = 1.dp,
- color = MaterialTheme.colorScheme.secondaryContainer,
- shape = shape,
- ),
- shape = shape,
- elevation = DesignToken.spacing.none,
- ) {
- Column(
- modifier = Modifier.padding(DesignToken.spacing.large),
+ if (state.notes.isEmpty()) {
+ MifosEmptyCard(
+ msg = stringResource(Res.string.feature_note_add_item),
+ )
+ } else {
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(DesignToken.spacing.small),
) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Text(
- text = stringResource(Res.string.feature_note_createdBy) + " " + createdByUsername,
- style = MifosTypography.titleSmallEmphasized,
- color = MaterialTheme.colorScheme.onSurface,
- )
-
- Icon(
- imageVector = MifosIcons.MoreHoriz,
- contentDescription = null,
- modifier = Modifier.clickable {
- id?.let { onAction(NoteAction.OnToggleExpanded(id)) }
- }.size(DesignToken.sizes.iconAverage),
- )
- }
-
- Spacer(
- modifier = Modifier.height(DesignToken.spacing.large),
- )
-
- Column {
- Text(
- text = stringResource(Res.string.feature_note_date),
- style = MifosTypography.labelSmall,
- color = MaterialTheme.colorScheme.secondary,
- )
+ items(state.notes.reversed()) { note ->
- Text(
- text = DateHelper.formatIsoDateToDdMmYyyy(createdOn ?: "Not found"),
- style = MaterialTheme.typography.bodySmall,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
+ MifosActionsNoteListingComponent(
+ notes = note.note ?: "Empty",
+ createdBy = note.createdByUsername ?: "Empty",
+ date = DateHelper.formatIsoDateToDdMmYyyy(note.createdOn ?: "Not found"),
+ isExpanded = state.expandedNoteId == note.id,
+ onExpand = {
+ onAction(NoteAction.OnToggleExpanded(note.id))
+ },
+ menuList = listOf(
+ Actions.Edit,
+ Actions.Delete,
+ ),
+ onActionClicked = { actions ->
+ when (actions) {
+ Actions.Edit -> {
+ onAction(NoteAction.OnClickEditScreen)
+ }
- Spacer(
- modifier = Modifier.height(DesignToken.spacing.medium),
- )
+ Actions.Delete -> {
+ onAction(NoteAction.ShowDialog)
+ }
- Column {
- Text(
- text = stringResource(Res.string.feature_note_note),
- style = MifosTypography.labelSmall,
- color = MaterialTheme.colorScheme.secondary,
+ else -> null
+ }
+ },
)
-
- if (note != null) {
- Text(
- text = note,
- style = MaterialTheme.typography.bodySmall,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
}
}
}
- AnimatedVisibility(
- visible = state.expandedNoteId == id,
- ) {
- ContextualActions(
- id = id,
- onAction = onAction,
- state = state,
- )
- }
}
}
-
-@Composable
-private fun ContextualActions(
- state: NoteState,
- id: Long?,
- onAction: (NoteAction) -> Unit,
-) {
- MifosCard(
- modifier = Modifier.fillMaxWidth(),
- shape = DesignToken.shapes.bottomMedium,
- colors = CardDefaults.cardColors(
- MaterialTheme.colorScheme.surfaceContainer,
- ),
- elevation = DesignToken.spacing.none,
- ) {
- Column(
- modifier = Modifier.padding(DesignToken.spacing.large),
- verticalArrangement = Arrangement.spacedBy(DesignToken.spacing.medium),
- ) {
- Row(
- modifier = Modifier.clickable {
- onAction(NoteAction.OnClickEditScreen)
- },
- horizontalArrangement = Arrangement.spacedBy(
- DesignToken.spacing.medium,
- ),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- imageVector = MifosIcons.Edit,
- contentDescription = null,
- modifier = Modifier.size(DesignToken.sizes.iconMedium),
- )
- Text(
- text = stringResource(Res.string.feature_note_edit_note),
- style = MaterialTheme.typography.bodyLarge,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
-
- Row(
- modifier = Modifier.clickable {
- onAction(NoteAction.ShowDialog)
- },
- horizontalArrangement = Arrangement.spacedBy(
- DesignToken.spacing.medium,
- ),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- imageVector = MifosIcons.Delete,
- contentDescription = null,
- modifier = Modifier.size(DesignToken.sizes.iconMedium),
- )
-
- Text(
- text = stringResource(Res.string.feature_note_delete_note),
- style = MaterialTheme.typography.bodyLarge,
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
- }
- }
-
- if (state.showDialog) {
- MifosAlertDialog(
- onDismissRequest = {
- onAction(NoteAction.DismissDialog)
- },
- onConfirmation = {
- onAction(NoteAction.DeleteNote(id))
- },
- confirmationText = stringResource(Res.string.feature_note_delete),
- dialogTitle = stringResource(Res.string.feature_note_delete_note),
- dialogText = stringResource(Res.string.feature_note_delete_note_confirmation),
- )
- }
-}
-
internal val demoNotes = listOf(
Note(
id = 1,
@@ -469,5 +303,6 @@ fun PreviewSuccessNoteScreen() {
NoteScreenScaffold(
onAction = {},
state = NoteState(notes = demoNotes),
+ navController = rememberNavController(),
)
}
diff --git a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteViewModel.kt b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteViewModel.kt
index 363e7907397..5ede765333d 100644
--- a/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteViewModel.kt
+++ b/feature/note/src/commonMain/kotlin/com/mifos/feature/note/notes/NoteViewModel.kt
@@ -65,7 +65,6 @@ class NoteViewModel(
is DataState.Error -> mutableStateFlow.update {
it.copy(
dialogState = NoteState.DialogState.Error(dataState.message),
- isError = true,
isRefreshing = false,
)
}
@@ -86,7 +85,6 @@ class NoteViewModel(
it.copy(
dialogState = null,
notes = dataState.data,
- isError = false,
expandedNoteId = null,
isRefreshing = false,
)
@@ -106,7 +104,6 @@ class NoteViewModel(
mutableStateFlow.update {
it.copy(
dialogState = NoteState.DialogState.Error(dataState.message),
- isError = true,
)
}
}
@@ -149,14 +146,8 @@ class NoteViewModel(
NoteAction.OnClickEditScreen -> sendEvent(NoteEvent.NavigateEditNote)
NoteAction.OnClickAddScreen -> sendEvent(NoteEvent.NavigateAddNote)
is NoteAction.DeleteNote -> {
- mutableStateFlow.update {
- it.copy(
- showDialog = false,
- isError = false,
- )
- }
viewModelScope.launch {
- deleteNote(action.id)
+ deleteNote(state.expandedNoteId)
}
}
@@ -167,15 +158,11 @@ class NoteViewModel(
getNoteOptionsAndObserveNetwork()
}
- NoteAction.ShowDialog -> {
- mutableStateFlow.update {
- it.copy(showDialog = true)
- }
- }
-
NoteAction.DismissDialog -> {
mutableStateFlow.update {
- it.copy(showDialog = false)
+ it.copy(
+ dialogState = null,
+ )
}
}
@@ -186,6 +173,14 @@ class NoteViewModel(
)
}
}
+
+ is NoteAction.ShowDialog -> {
+ mutableStateFlow.update {
+ it.copy(
+ dialogState = NoteState.DialogState.ShowDialog,
+ )
+ }
+ }
}
}
}
@@ -195,15 +190,14 @@ data class NoteState(
val resourceType: String? = null,
val isRefreshing: Boolean = false,
val notes: List = emptyList(),
- val showDialog: Boolean = false,
val dialogState: DialogState? = null,
val expandedNoteId: Long? = null,
- val isError: Boolean = false,
val networkConnection: Boolean = false,
) {
sealed interface DialogState {
data class Error(val message: String) : DialogState
data object Loading : DialogState
+ data object ShowDialog : DialogState
}
}
@@ -222,5 +216,5 @@ sealed interface NoteAction {
data object ShowDialog : NoteAction
data object DismissDialog : NoteAction
data class OnToggleExpanded(val id: Long?) : NoteAction
- data class DeleteNote(val id: Long?) : NoteAction
+ data object DeleteNote : NoteAction
}