Moved standingInstruction to feature module (#1671)

Co-authored-by: Rajan Maurya <therajanmaurya@users.noreply.github.com>
This commit is contained in:
Aditya Kumdale 2024-06-20 20:40:48 +05:30 committed by GitHub
parent 7b8876306e
commit 23649dd8d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 373 additions and 0 deletions

View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,12 @@
plugins {
alias(libs.plugins.mifospay.android.feature)
alias(libs.plugins.mifospay.android.library.compose)
}
android {
namespace = "org.mifospay.feature.standing.instruction"
}
dependencies {
implementation(projects.core.data)
}

View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package org.mifospay.feature.standing.instruction
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("org.mifospay.feature.standing.instruction.test", appContext.packageName)
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View File

@ -0,0 +1,65 @@
package org.mifospay.feature.standing.instruction
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Divider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun SIContent(
fromClientName: String,
toClientName: String,
validTill: String,
amount: String
) {
Column(modifier = Modifier.padding(10.dp)) {
Text(
text = fromClientName,
color = Color.Black,
fontSize = 16.sp,
modifier = Modifier.padding(bottom = 20.dp)
)
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Text(
text = toClientName,
color = Color.Black,
fontSize = 16.sp,
modifier = Modifier.padding(bottom = 4.dp)
)
Text(
text = amount,
color = Color.Black,
fontSize = 16.sp,
modifier = Modifier.padding(end = 8.dp, bottom = 8.dp)
)
}
Text(
text = validTill,
color = Color.Gray,
modifier = Modifier.padding(bottom = 4.dp)
)
Divider(
color = Color.Black,
thickness = 1.dp,
modifier = Modifier.padding(vertical = 8.dp)
)
}
}
@Preview(showBackground = true)
@Composable
fun SIContentPreview() {
SIContent("From Client", "To Client", "Date", "Amount")
}

View File

@ -0,0 +1,162 @@
package org.mifospay.feature.standing.instruction
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Info
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.mifospay.core.model.entity.accounts.savings.SavingAccount
import com.mifospay.core.model.entity.client.Client
import com.mifospay.core.model.entity.client.Status
import com.mifospay.core.model.entity.standinginstruction.StandingInstruction
import org.mifospay.core.designsystem.component.MifosLoadingWheel
import org.mifospay.core.designsystem.icon.MifosIcons
import org.mifospay.core.ui.EmptyContentScreen
@Composable
fun StandingInstructionsScreen(
viewModel: StandingInstructionViewModel = hiltViewModel(),
onNewSI: () -> Unit
) {
val standingInstructionsUiState by viewModel.standingInstructionsUiState.collectAsStateWithLifecycle()
StandingInstructionScreen(
standingInstructionsUiState = standingInstructionsUiState,
onNewSI = onNewSI
)
}
@Composable
fun StandingInstructionScreen(
standingInstructionsUiState: StandingInstructionsUiState,
onNewSI: () -> Unit
) = when (standingInstructionsUiState) {
StandingInstructionsUiState.Empty -> {
EmptyContentScreen(
modifier = Modifier,
title = stringResource(id = R.string.feature_standing_instruction_error_oops),
subTitle = stringResource(id = R.string.feature_standing_instruction_empty_standing_instructions),
iconTint = Color.Black,
iconImageVector = Icons.Rounded.Info
)
}
is StandingInstructionsUiState.Error -> {
EmptyContentScreen(
modifier = Modifier,
title = stringResource(id = R.string.feature_standing_instruction_error_oops),
subTitle = stringResource(id = R.string.feature_standing_instruction_error_fetching_si_list),
iconTint = Color.Black,
iconImageVector = Icons.Rounded.Info
)
}
StandingInstructionsUiState.Loading -> {
MifosLoadingWheel(
modifier = Modifier.fillMaxWidth(),
contentDesc = stringResource(R.string.feature_standing_instruction_loading)
)
}
is StandingInstructionsUiState.StandingInstructionList -> {
Scaffold(
modifier = Modifier,
floatingActionButton = {
FloatingActionButton(
onClick = { onNewSI.invoke() },
) {
Icon(
painter = rememberVectorPainter(MifosIcons.Add),
contentDescription = null,
tint = Color.Black
)
}
}
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(it)
) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(standingInstructionsUiState.standingInstructionList) { items ->
SIContent(
fromClientName = items.fromClient.displayName.toString(),
toClientName = items.toClient.displayName.toString(),
validTill = items.validTill.toString(),
amount = items.amount.toString(),
)
}
}
}
}
}
}
class StandingInstructionPreviewParameterProvider : PreviewParameterProvider<StandingInstructionsUiState> {
override val values: Sequence<StandingInstructionsUiState> = sequenceOf(
StandingInstructionsUiState.Loading,
StandingInstructionsUiState.Empty,
StandingInstructionsUiState.Error("Error Screen"),
StandingInstructionsUiState.StandingInstructionList(
standingInstructionList = listOf(
StandingInstruction(
id = 1,
name = "Instruction 1",
fromClient = Client(displayName = "Alice"),
fromAccount = SavingAccount(),
toClient = Client(displayName = "Bob"),
toAccount = SavingAccount(),
status = Status(),
amount = 100.0,
validFrom = listOf(2022, 1, 1),
validTill = listOf(2024, 12, 31),
recurrenceInterval = 30,
recurrenceOnMonthDay = listOf(1)
),
StandingInstruction(
id = 2,
name = "Instruction 2",
fromClient = Client(displayName = "Charlie"),
fromAccount = SavingAccount(),
toClient = Client(displayName = "Dave"),
toAccount = SavingAccount(),
status = Status(),
amount = 200.0,
validFrom = listOf(2022, 1, 1),
validTill = listOf(2024, 12, 31),
recurrenceInterval = 30,
recurrenceOnMonthDay = listOf(1)
)
)
)
)
}
@Preview(showBackground = true)
@Composable
fun StandingInstructionsScreenPreview(
@PreviewParameter(StandingInstructionPreviewParameterProvider::class) standingInstructionsUiState: StandingInstructionsUiState
) {
StandingInstructionScreen(
standingInstructionsUiState = standingInstructionsUiState,
onNewSI = {}
)
}

View File

@ -0,0 +1,59 @@
package org.mifospay.feature.standing.instruction
import androidx.lifecycle.ViewModel
import com.mifospay.core.model.entity.standinginstruction.StandingInstruction
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.mifospay.core.data.base.UseCase
import org.mifospay.core.data.base.UseCaseHandler
import org.mifospay.core.data.domain.usecase.standinginstruction.GetAllStandingInstructions
import org.mifospay.core.data.repository.local.LocalRepository
import javax.inject.Inject
@HiltViewModel
class StandingInstructionViewModel @Inject constructor(
private val mUseCaseHandler: UseCaseHandler,
private val localRepository: LocalRepository,
private val getAllStandingInstructions: GetAllStandingInstructions
) : ViewModel() {
private val _standingInstructionsUiState =
MutableStateFlow<StandingInstructionsUiState>(StandingInstructionsUiState.Loading)
val standingInstructionsUiState: StateFlow<StandingInstructionsUiState> =
_standingInstructionsUiState
private fun getAllSI() {
val client = localRepository.clientDetails
_standingInstructionsUiState.value = StandingInstructionsUiState.Loading
mUseCaseHandler.execute(getAllStandingInstructions,
GetAllStandingInstructions.RequestValues(client.clientId), object :
UseCase.UseCaseCallback<GetAllStandingInstructions.ResponseValue> {
override fun onSuccess(response: GetAllStandingInstructions.ResponseValue) {
if (response.standingInstructionsList.isEmpty()) {
_standingInstructionsUiState.value = StandingInstructionsUiState.Empty
} else {
_standingInstructionsUiState.value =
StandingInstructionsUiState.StandingInstructionList(response.standingInstructionsList)
}
}
override fun onError(message: String) {
_standingInstructionsUiState.value = StandingInstructionsUiState.Error(message)
}
})
}
init {
getAllSI()
}
}
sealed class StandingInstructionsUiState {
data object Loading : StandingInstructionsUiState()
data object Empty : StandingInstructionsUiState()
data class Error(val message: String) : StandingInstructionsUiState()
data class StandingInstructionList(val standingInstructionList: List<StandingInstruction>) :
StandingInstructionsUiState()
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="feature_standing_instruction_error_oops">Oops!</string>
<string name="feature_standing_instruction_empty_standing_instructions">No standing instructions to show</string>
<string name="feature_standing_instruction_error_fetching_si_list">Couldn\'t fetch Standing Instructions. Please, try again.</string>
<string name="feature_standing_instruction_loading">Loading</string>
</resources>

View File

@ -0,0 +1,17 @@
package org.mifospay.feature.standing.instruction
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@ -48,4 +48,5 @@ include(":feature:invoices")
include(":feature:invoices")
include(":feature:settings")
include(":feature:profile")
include(":feature:standing-instruction")
include(":feature:payments")