Skip to content

Commit a8e9954

Browse files
authored
Implement Share Account Preview Screen (#2549)
1 parent a2c7390 commit a8e9954

File tree

10 files changed

+343
-29
lines changed

10 files changed

+343
-29
lines changed

core/data/src/commonMain/kotlin/com/mifos/core/data/repository/ShareAccountRepository.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
package com.mifos.core.data.repository
1111

1212
import com.mifos.core.common.utils.DataState
13+
import com.mifos.core.network.GenericResponse
14+
import com.mifos.core.network.model.share.ShareAccountPayload
1315
import com.mifos.core.network.model.share.ShareTemplate
1416
import kotlinx.coroutines.flow.Flow
1517

1618
interface ShareAccountRepository {
1719

1820
fun getShareTemplate(clientId: Int, productId: Int?): Flow<DataState<ShareTemplate>>
21+
suspend fun createShareAccount(shareAccountPayload: ShareAccountPayload): Flow<DataState<GenericResponse>>
1922
}

core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/ShareAccountRepositoryImpl.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ package com.mifos.core.data.repositoryImp
1212
import com.mifos.core.common.utils.DataState
1313
import com.mifos.core.common.utils.asDataStateFlow
1414
import com.mifos.core.data.repository.ShareAccountRepository
15+
import com.mifos.core.network.GenericResponse
1516
import com.mifos.core.network.datamanager.DataManagerShare
17+
import com.mifos.core.network.model.share.ShareAccountPayload
1618
import com.mifos.core.network.model.share.ShareTemplate
1719
import kotlinx.coroutines.flow.Flow
20+
import kotlinx.coroutines.flow.flow
1821

1922
class ShareAccountRepositoryImpl(
2023
private val dataManagerShare: DataManagerShare,
@@ -23,4 +26,12 @@ class ShareAccountRepositoryImpl(
2326
override fun getShareTemplate(clientId: Int, productId: Int?): Flow<DataState<ShareTemplate>> {
2427
return dataManagerShare.getShareTemplate(clientId, productId).asDataStateFlow()
2528
}
29+
30+
override suspend fun createShareAccount(
31+
shareAccountPayload: ShareAccountPayload,
32+
): Flow<DataState<GenericResponse>> {
33+
return flow {
34+
emit(dataManagerShare.createShareAccount(shareAccountPayload))
35+
}.asDataStateFlow()
36+
}
2637
}

core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerShare.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,35 @@
99
*/
1010
package com.mifos.core.network.datamanager
1111

12+
import com.mifos.core.common.utils.extractErrorMessage
1213
import com.mifos.core.network.BaseApiManager
14+
import com.mifos.core.network.GenericResponse
15+
import com.mifos.core.network.model.share.ShareAccountPayload
1316
import com.mifos.core.network.model.share.ShareTemplate
17+
import io.ktor.client.statement.bodyAsText
18+
import io.ktor.http.isSuccess
19+
import io.ktor.serialization.kotlinx.json.json
1420
import kotlinx.coroutines.flow.Flow
21+
import kotlinx.serialization.json.Json
1522

1623
class DataManagerShare(
1724
private val baseApiManager: BaseApiManager,
1825
) {
1926

2027
fun getShareTemplate(clientId: Int, productId: Int?): Flow<ShareTemplate> =
2128
baseApiManager.shareAccountService.shareProductTemplate(clientId, productId)
29+
30+
suspend fun createShareAccount(shareAccountPayload: ShareAccountPayload): GenericResponse {
31+
val response = baseApiManager.shareAccountService.createShareAccount(shareAccountPayload)
32+
33+
return if (!response.status.isSuccess()) {
34+
val errorMsg = extractErrorMessage(response)
35+
36+
throw IllegalStateException(errorMsg)
37+
} else {
38+
val json = Json { ignoreUnknownKeys = true }
39+
40+
json.decodeFromString<GenericResponse>(response.bodyAsText())
41+
}
42+
}
2243
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
9+
*/
10+
package com.mifos.core.network.model.share
11+
12+
import kotlinx.serialization.Serializable
13+
14+
@Serializable
15+
data class ShareAccountPayload(
16+
val clientId: Int,
17+
18+
val productId: Int? = null,
19+
20+
val requestedShares: Int,
21+
22+
val externalId: String? = null,
23+
24+
val submittedDate: String,
25+
26+
val minimumActivePeriod: Int? = null,
27+
28+
val minimumActivePeriodFrequencyType: Int? = null,
29+
30+
val lockinPeriodFrequency: Int? = null,
31+
32+
val lockinPeriodFrequencyType: Int? = null,
33+
34+
val applicationDate: String,
35+
36+
val allowDividendCalculationForInactiveClients: Boolean,
37+
38+
val locale: String? = null,
39+
40+
val dateFormat: String? = null,
41+
42+
val charges: List<ChargeItem> = emptyList(),
43+
44+
val savingsAccountId: Int,
45+
)
46+
47+
@Serializable
48+
data class ChargeItem(
49+
val chargeId: Int? = null,
50+
51+
val amount: Double? = null,
52+
)

core/network/src/commonMain/kotlin/com/mifos/core/network/services/ShareAccountService.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,25 @@
99
*/
1010
package com.mifos.core.network.services
1111

12+
import com.mifos.core.network.model.share.ShareAccountPayload
1213
import com.mifos.core.network.model.share.ShareTemplate
1314
import com.mifos.room.basemodel.APIEndPoint
15+
import de.jensklingenberg.ktorfit.http.Body
1416
import de.jensklingenberg.ktorfit.http.GET
17+
import de.jensklingenberg.ktorfit.http.POST
1518
import de.jensklingenberg.ktorfit.http.Query
19+
import io.ktor.client.statement.HttpResponse
1620
import kotlinx.coroutines.flow.Flow
1721

1822
interface ShareAccountService {
19-
2023
@GET("accounts/" + APIEndPoint.SHARE + "/template")
2124
fun shareProductTemplate(
2225
@Query("clientId") clientId: Int,
2326
@Query("productId") productId: Int?,
2427
): Flow<ShareTemplate>
28+
29+
@POST("accounts/" + APIEndPoint.SHARE)
30+
suspend fun createShareAccount(
31+
@Body shareAccountPayload: ShareAccountPayload,
32+
): HttpResponse
2533
}

feature/client/src/commonMain/composeResources/values/strings.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,9 +551,11 @@
551551
<string name="feature_share_account_preview">Preview</string>
552552
<string name="feature_share_account_back">Back</string>
553553
<string name="feature_share_account_next">Next</string>
554+
<string name="feature_share_account_submit">Submit</string>
555+
<string name="feature_share_account_created_successfully">Share account created successfully</string>
554556

555-
<string name="feature_share_account_detail_product_name">Product Name*</string>
556-
<string name="feature_share_account_detail_submission_date">Submission Date*</string>
557+
<string name="feature_share_account_detail_product_name">Product Name</string>
558+
<string name="feature_share_account_detail_submission_date">Submission Date</string>
557559
<string name="feature_share_account_detail_external_id">External Id</string>
558560
<string name="feature_share_account_detail_date_select">Select</string>
559561
<string name="feature_share_account_detail_date_cancel">Cancel</string>

feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/CreateShareAccountScreen.kt

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,20 @@ import androidx.compose.foundation.layout.Column
2525
import androidx.compose.foundation.layout.fillMaxSize
2626
import androidx.compose.foundation.layout.fillMaxWidth
2727
import androidx.compose.foundation.layout.padding
28+
import androidx.compose.material3.SnackbarHostState
2829
import androidx.compose.material3.Text
2930
import androidx.compose.runtime.Composable
31+
import androidx.compose.runtime.LaunchedEffect
3032
import androidx.compose.runtime.getValue
3133
import androidx.compose.runtime.mutableStateOf
34+
import androidx.compose.runtime.remember
3235
import androidx.compose.runtime.saveable.rememberSaveable
3336
import androidx.compose.runtime.setValue
3437
import androidx.compose.ui.Alignment
3538
import androidx.compose.ui.Modifier
3639
import androidx.lifecycle.compose.collectAsStateWithLifecycle
3740
import androidx.navigation.NavController
41+
import co.touchlab.kermit.Logger
3842
import com.mifos.core.designsystem.component.MifosBottomSheet
3943
import com.mifos.core.designsystem.component.MifosScaffold
4044
import com.mifos.core.designsystem.component.MifosSweetError
@@ -54,6 +58,7 @@ import com.mifos.feature.client.createShareAccount.pages.ChargesPage
5458
import com.mifos.feature.client.createShareAccount.pages.DetailsPage
5559
import com.mifos.feature.client.createShareAccount.pages.PreviewPage
5660
import com.mifos.feature.client.createShareAccount.pages.TermsPage
61+
import kotlinx.coroutines.delay
5762
import org.jetbrains.compose.resources.stringResource
5863
import org.koin.compose.viewmodel.koinViewModel
5964

@@ -67,30 +72,35 @@ internal fun CreateShareAccountScreen(
6772
) {
6873
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
6974

75+
val snackbarHostState = remember { SnackbarHostState() }
76+
7077
EventsEffect(viewModel.eventFlow) { event ->
7178
when (event) {
7279
CreateShareAccountEvent.NavigateBack -> onNavigateBack()
7380
CreateShareAccountEvent.Finish -> onFinish()
7481
}
7582
}
7683

77-
CreateShareAccountContent(
78-
modifier = modifier,
84+
CreateShareAccountDialog(
7985
state = state,
8086
onAction = { viewModel.trySendAction(it) },
81-
navController = navController,
87+
snackbarHostState = snackbarHostState,
8288
)
8389

84-
CreateShareAccountDialog(
90+
CreateShareAccountContent(
91+
modifier = modifier,
8592
state = state,
8693
onAction = { viewModel.trySendAction(it) },
94+
navController = navController,
95+
snackbarHostState = snackbarHostState,
8796
)
8897
}
8998

9099
@Composable
91100
fun CreateShareAccountDialog(
92101
state: CreateShareAccountState,
93102
onAction: (CreateShareAccountAction) -> Unit,
103+
snackbarHostState: SnackbarHostState,
94104
) {
95105
when (state.dialogState) {
96106
is CreateShareAccountState.DialogState.AddNewCharge -> {
@@ -108,7 +118,20 @@ fun CreateShareAccountDialog(
108118
onAction = onAction,
109119
)
110120
}
121+
is CreateShareAccountState.DialogState.SuccessResponseStatus -> {
122+
LaunchedEffect(state.launchEffectKey) {
123+
snackbarHostState.showSnackbar(
124+
message = state.dialogState.msg,
125+
)
111126

127+
Logger.d("SuccessResponseStatus")
128+
129+
if (state.dialogState.successStatus) {
130+
delay(1000)
131+
onAction(CreateShareAccountAction.NavigateBack)
132+
}
133+
}
134+
}
112135
null -> Unit
113136
}
114137
}
@@ -119,6 +142,7 @@ private fun CreateShareAccountContent(
119142
modifier: Modifier = Modifier,
120143
onAction: (CreateShareAccountAction) -> Unit,
121144
navController: NavController,
145+
snackbarHostState: SnackbarHostState,
122146
) {
123147
val steps = listOf(
124148
Step(name = stringResource(Res.string.feature_share_account_details)) {
@@ -141,13 +165,15 @@ private fun CreateShareAccountContent(
141165
},
142166
Step(name = stringResource(Res.string.feature_share_account_preview)) {
143167
PreviewPage(
144-
onNext = { onAction(CreateShareAccountAction.Finish) },
168+
state = state,
169+
onAction = onAction,
145170
)
146171
},
147172
)
148173

149174
MifosScaffold(
150175
modifier = modifier,
176+
snackbarHostState = snackbarHostState,
151177
) { paddingValues ->
152178
when (state.screenState) {
153179
is CreateShareAccountState.ScreenState.Loading -> MifosProgressIndicator()

0 commit comments

Comments
 (0)