Move Merchants to feature module (#1669)

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

1
feature/merchants/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,19 @@
plugins {
alias(libs.plugins.mifospay.android.feature)
alias(libs.plugins.mifospay.android.library.compose)
}
android {
namespace = "org.mifospay.feature.merchants"
}
dependencies {
implementation(projects.core.data)
implementation(libs.compose.material)
//Todo: Remove these after migration of MerchantTransferActivity
implementation("com.jakewharton:butterknife-annotations:10.2.3")
implementation("com.jakewharton:butterknife:10.2.3@aar")
implementation("com.mifos.mobile:mifos-passcode:0.3.0@aar")
implementation(project(":mifospay"))
}

View File

21
feature/merchants/proguard-rules.pro vendored Normal file
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.merchants
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.merchants.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,284 @@
package org.mifospay.feature.merchants
import android.content.Intent
import android.widget.Toast
import androidx.compose.foundation.layout.Box
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.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.rounded.Info
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.SearchBar
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.mifospay.core.model.entity.accounts.savings.SavingsWithAssociations
import org.mifospay.R
import org.mifospay.common.Constants
import org.mifospay.core.designsystem.component.MfLoadingWheel
import org.mifospay.core.ui.EmptyContentScreen
import org.mifospay.theme.MifosTheme
@Composable
fun MerchantScreen(
viewModel: MerchantViewModel = hiltViewModel()
) {
val merchantUiState by viewModel.merchantUiState.collectAsStateWithLifecycle()
val merchantsListUiState by viewModel.merchantsListUiState.collectAsStateWithLifecycle()
val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle()
MerchantScreen(
merchantUiState = merchantUiState,
merchantListUiState = merchantsListUiState,
updateQuery = { viewModel.updateSearchQuery(it) },
isRefreshing = isRefreshing,
onRefresh = { viewModel.refresh() },
)
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MerchantScreen(
merchantUiState: MerchantUiState,
merchantListUiState: MerchantUiState,
updateQuery: (String) -> Unit,
isRefreshing: Boolean,
onRefresh: () -> Unit,
) {
val pullRefreshState = rememberPullRefreshState(isRefreshing, onRefresh)
Box(Modifier.pullRefresh(pullRefreshState)) {
Column(modifier = Modifier.fillMaxSize()) {
when (merchantUiState) {
MerchantUiState.Empty -> {
EmptyContentScreen(
modifier = Modifier,
title = stringResource(id = R.string.empty_no_merchants_title),
subTitle = stringResource(id = R.string.empty_no_merchants_subtitle),
iconTint = Color.Black,
iconImageVector = Icons.Rounded.Info
)
}
is MerchantUiState.Error -> {
EmptyContentScreen(
modifier = Modifier,
title = stringResource(id = R.string.error_oops),
subTitle = stringResource(id = R.string.unexpected_error_subtitle),
iconTint = Color.Black,
iconImageVector = Icons.Rounded.Info
)
}
MerchantUiState.Loading -> {
MfLoadingWheel(
contentDesc = stringResource(R.string.loading),
backgroundColor = Color.White
)
}
is MerchantUiState.ShowMerchants -> {
MerchantScreenContent(
merchantList = (merchantListUiState as MerchantUiState.ShowMerchants).merchants,
updateQuery = updateQuery
)
}
}
}
PullRefreshIndicator(
refreshing = isRefreshing,
state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
}
}
@Composable
fun MerchantScreenContent(
merchantList: List<SavingsWithAssociations>,
updateQuery: (String) -> Unit
) {
val query by rememberSaveable { mutableStateOf("") }
Box(modifier = Modifier.fillMaxSize()) {
Column {
SearchBarScreen(
query = query,
onQueryChange = { q ->
updateQuery(q)
},
onSearch = {},
onClearQuery = { updateQuery("") }
)
MerchantList(merchantList = merchantList)
}
}
}
@Composable
fun MerchantList(
merchantList: List<SavingsWithAssociations>
) {
val context = LocalContext.current
val clipboardManager = LocalClipboardManager.current
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
items(merchantList.size) { index ->
MerchantsItem(savingsWithAssociations = merchantList[index],
onMerchantClicked = {
val intent = Intent(context, MerchantTransferActivity::class.java)
intent.putExtra(Constants.MERCHANT_NAME, merchantList[index].clientName)
intent.putExtra(Constants.MERCHANT_VPA, merchantList[index].externalId)
intent.putExtra(Constants.MERCHANT_ACCOUNT_NO, merchantList[index].accountNo)
context.startActivity(intent)
},
onMerchantLongPressed = {
clipboardManager.setText(AnnotatedString(it ?: ""))
Toast.makeText(context, R.string.vpa_copy_success, Toast.LENGTH_LONG).show()
}
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SearchBarScreen(
query: String,
onQueryChange: (String) -> Unit,
onSearch: (String) -> Unit,
onClearQuery: () -> Unit
) {
SearchBar(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp, horizontal = 16.dp),
query = query,
onQueryChange = onQueryChange,
onSearch = onSearch,
active = false,
onActiveChange = { },
placeholder = {
Text(text = stringResource(R.string.search))
},
leadingIcon = {
Icon(
imageVector = Icons.Filled.Search,
contentDescription = stringResource(R.string.search)
)
},
trailingIcon = {
IconButton(
onClick = onClearQuery
) {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = stringResource(R.string.close)
)
}
}
) {}
}
@Preview(showBackground = true)
@Composable
private fun MerchantLoadingPreview() {
MifosTheme {
MerchantScreen(merchantUiState = MerchantUiState.Loading,
merchantListUiState = MerchantUiState.ShowMerchants(sampleMerchantList),
updateQuery = {},
false, {}
)
}
}
@Preview(showBackground = true)
@Composable
private fun MerchantListPreview() {
MifosTheme {
MerchantScreen(
merchantUiState = MerchantUiState.ShowMerchants(sampleMerchantList),
merchantListUiState = MerchantUiState.ShowMerchants(sampleMerchantList),
updateQuery = {}, false, {}
)
}
}
@Preview(showBackground = true)
@Composable
private fun MerchantErrorPreview() {
MifosTheme {
MerchantScreen(
merchantUiState = MerchantUiState.Error("Error Screen"),
merchantListUiState = MerchantUiState.ShowMerchants(sampleMerchantList),
updateQuery = {}, true, {}
)
}
}
@Preview(showBackground = true)
@Composable
private fun MerchantEmptyPreview() {
MifosTheme {
MerchantScreen(
merchantUiState = MerchantUiState.Empty,
merchantListUiState = MerchantUiState.ShowMerchants(sampleMerchantList),
updateQuery = {}, false, {}
)
}
}
val sampleMerchantList = List(10) {
SavingsWithAssociations(
id = 1L,
accountNo = "123456789",
depositType = null,
externalId = "EXT987654",
clientId = 101,
clientName = "Alice Bob",
savingsProductId = 2001,
savingsProductName = "Premium Savings Account",
fieldOfficerId = 501,
status = null,
timeline = null,
currency = null,
nominalAnnualInterestRate = 3.5,
minRequiredOpeningBalance = 500.0,
lockinPeriodFrequency = 12.0,
withdrawalFeeForTransfers = true,
allowOverdraft = false,
enforceMinRequiredBalance = false,
withHoldTax = true,
lastActiveTransactionDate = listOf(2024, 3, 24),
dormancyTrackingActive = true,
summary = null,
transactions = listOf()
)
}

View File

@ -0,0 +1,198 @@
package org.mifospay.feature.merchants
import android.os.Bundle
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
import com.google.android.material.textfield.TextInputEditText
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import butterknife.BindView
import butterknife.ButterKnife
import butterknife.OnClick
import dagger.hilt.android.AndroidEntryPoint
import com.mifospay.core.model.domain.Transaction
import org.mifospay.MifosPayApp
import org.mifospay.R
import org.mifospay.base.BaseActivity
import org.mifospay.common.ui.MakeTransferFragment
import org.mifospay.history.ui.adapter.SpecificTransactionsAdapter
import org.mifospay.merchants.presenter.MerchantTransferPresenter
import org.mifospay.common.Constants
import org.mifospay.home.BaseHomeContract
import org.mifospay.utils.TextDrawable
import org.mifospay.utils.Toaster
import javax.inject.Inject
/**
* Created by Shivansh Tiwari on 06/07/19.
*/
@AndroidEntryPoint
class MerchantTransferActivity : BaseActivity(), BaseHomeContract.MerchantTransferView {
private var mBottomSheetBehavior: BottomSheetBehavior<*>? = null
@JvmField
@BindView(R.id.nsv_merchant_bottom_sheet_dialog)
var vMerchantBottomSheetDialog: View? = null
@JvmField
@BindView(R.id.iv_merchant_image)
var ivMerchantImage: ImageView? = null
@JvmField
@BindView(R.id.tv_pay_to_name)
var tvMerchantName: TextView? = null
@JvmField
@BindView(R.id.tv_pay_to_vpa)
var tvMerchantVPA: TextView? = null
@JvmField
@BindView(R.id.et_merchant_amount)
var etAmount: TextInputEditText? = null
@JvmField
@BindView(R.id.btn_submit)
var btnSubmit: Button? = null
@JvmField
@BindView(R.id.rv_merchant_history)
var rvMerchantHistory: RecyclerView? = null
@JvmField
@BindView(R.id.inc_empty_transactions_state_view)
var vEmptyState: View? = null
@JvmField
@BindView(R.id.iv_empty_no_transaction_history)
var ivTransactionsStateIcon: ImageView? = null
@JvmField
@BindView(R.id.tv_empty_no_transaction_history_title)
var tvTransactionsStateTitle: TextView? = null
@JvmField
@BindView(R.id.tv_empty_no_transaction_history_subtitle)
var tvTransactionsStateSubtitle: TextView? = null
@JvmField
@Inject
var mPresenter: MerchantTransferPresenter? = null
private var mTransferPresenter: BaseHomeContract.MerchantTransferPresenter? = null
private var merchantAccountNumber: String? = null
@JvmField
@Inject
var mMerchantHistoryAdapter: SpecificTransactionsAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_merchant_transaction)
ButterKnife.bind(this)
setToolbarTitle("Merchant Transaction")
showColoredBackButton(R.drawable.ic_arrow_back_black_24dp)
setupUI()
mPresenter?.attachView(this)
mPresenter?.fetchMerchantTransfers(merchantAccountNumber)
}
private fun setupUI() {
setupBottomSheet()
merchantAccountNumber = intent.getStringExtra(Constants.MERCHANT_ACCOUNT_NO)
tvMerchantName?.text =
intent.getStringExtra(Constants.MERCHANT_NAME)
tvMerchantVPA?.text = intent.getStringExtra(Constants.MERCHANT_VPA)
val drawable = intent.getStringExtra(Constants.MERCHANT_NAME)
?.substring(0, 1)?.let {
TextDrawable.builder().beginConfig()
.width(resources.getDimension(R.dimen.user_profile_image_size).toInt())
.height(resources.getDimension(R.dimen.user_profile_image_size).toInt())
.endConfig().buildRound(
it, R.color.colorPrimary
)
}
ivMerchantImage?.setImageDrawable(drawable)
showTransactionFetching()
setUpRecycleView()
}
private fun setUpRecycleView() {
mMerchantHistoryAdapter?.setContext(this)
rvMerchantHistory?.layoutManager =
LinearLayoutManager(MifosPayApp.context)
rvMerchantHistory?.adapter = mMerchantHistoryAdapter
}
override fun setPresenter(presenter: BaseHomeContract.MerchantTransferPresenter?) {
mTransferPresenter = presenter
}
private fun setupBottomSheet() {
mBottomSheetBehavior = BottomSheetBehavior.from(vMerchantBottomSheetDialog!!)
mBottomSheetBehavior?.setBottomSheetCallback(object : BottomSheetCallback() {
override fun onStateChanged(view: View, newState: Int) {
when (newState) {
BottomSheetBehavior.STATE_COLLAPSED -> {}
else -> {}
}
}
override fun onSlide(view: View, v: Float) {}
})
}
@OnClick(R.id.btn_submit)
fun makeTransaction() {
val externalId = tvMerchantVPA?.text.toString().trim { it <= ' ' }
val amount = etAmount?.text.toString().trim { it <= ' ' }
if (amount.isEmpty()) {
showToast(Constants.PLEASE_ENTER_ALL_THE_FIELDS)
return
} else if (amount.toDouble() <= 0) {
showToast(Constants.PLEASE_ENTER_VALID_AMOUNT)
return
}
mTransferPresenter?.checkBalanceAvailability(externalId, amount.toDouble())
}
override fun onBackPressed() {
if (mBottomSheetBehavior?.state != BottomSheetBehavior.STATE_COLLAPSED) {
mBottomSheetBehavior?.state = BottomSheetBehavior.STATE_COLLAPSED
return
}
super.onBackPressed()
}
override fun showToast(message: String?) {
Toaster.showToast(MifosPayApp.context, message)
}
override fun showPaymentDetails(externalId: String?, amount: Double) {
val fragment = MakeTransferFragment.newInstance(externalId, amount)
fragment.show(supportFragmentManager, "tag")
}
override fun showTransactionFetching() {
rvMerchantHistory?.visibility = View.GONE
tvTransactionsStateTitle?.text = resources.getString(R.string.fetching)
tvTransactionsStateSubtitle?.visibility = View.GONE
ivTransactionsStateIcon?.visibility = View.GONE
}
override fun showTransactions(transactions: List<Transaction?>?) {
vEmptyState?.visibility = View.GONE
rvMerchantHistory?.visibility = View.VISIBLE
mMerchantHistoryAdapter?.setData(transactions as List<Transaction>)
}
override fun showSpecificView(drawable: Int, title: Int, subtitle: Int) {
rvMerchantHistory?.visibility = View.GONE
tvTransactionsStateSubtitle?.visibility = View.VISIBLE
ivTransactionsStateIcon?.visibility = View.VISIBLE
tvTransactionsStateTitle?.setText(title)
tvTransactionsStateSubtitle?.setText(subtitle)
ivTransactionsStateIcon?.setImageDrawable(resources.getDrawable(drawable))
}
}

View File

@ -0,0 +1,144 @@
package org.mifospay.feature.merchants
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mifospay.core.model.entity.accounts.savings.SavingsWithAssociations
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.mifospay.core.data.base.TaskLooper
import org.mifospay.core.data.base.UseCase
import org.mifospay.core.data.base.UseCaseFactory
import org.mifospay.core.data.base.UseCaseHandler
import org.mifospay.core.data.domain.usecase.account.FetchMerchants
import org.mifospay.core.data.domain.usecase.client.FetchClientDetails
import org.mifospay.core.data.util.Constants
import javax.inject.Inject
@HiltViewModel
class MerchantViewModel @Inject constructor(
private val mUseCaseHandler: UseCaseHandler,
private val mFetchMerchantsUseCase: FetchMerchants,
private val mUseCaseFactory: UseCaseFactory
) : ViewModel() {
@Inject
lateinit var mTaskLooper: TaskLooper
private val _searchQuery = MutableStateFlow("")
private val searchQuery: StateFlow<String> = _searchQuery.asStateFlow()
private val _merchantUiState = MutableStateFlow<MerchantUiState>(MerchantUiState.Loading)
val merchantUiState: StateFlow<MerchantUiState> = _merchantUiState
init {
fetchMerchants()
}
private val _isRefreshing = MutableStateFlow(false)
val isRefreshing: StateFlow<Boolean> get() = _isRefreshing.asStateFlow()
fun refresh() {
viewModelScope.launch {
_isRefreshing.emit(true)
fetchMerchants()
_isRefreshing.emit(false)
}
}
val merchantsListUiState: StateFlow<MerchantUiState> = searchQuery
.map { q ->
when (_merchantUiState.value) {
is MerchantUiState.ShowMerchants -> {
val merchantList =
(merchantUiState.value as MerchantUiState.ShowMerchants).merchants
val filterCards = merchantList.filter {
it.externalId.lowercase().contains(q.lowercase())
it.savingsProductName?.lowercase()?.contains(q.lowercase())
it.accountNo?.lowercase()?.contains(q.lowercase())
it.clientName.lowercase().contains(q.lowercase())
}
MerchantUiState.ShowMerchants(filterCards)
}
else -> MerchantUiState.ShowMerchants(arrayListOf())
}
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = MerchantUiState.ShowMerchants(arrayListOf())
)
fun updateSearchQuery(query: String) {
_searchQuery.update { query }
}
private fun fetchMerchants() {
_merchantUiState.value = MerchantUiState.Loading
mUseCaseHandler.execute(mFetchMerchantsUseCase,
FetchMerchants.RequestValues(),
object : UseCase.UseCaseCallback<FetchMerchants.ResponseValue> {
override fun onSuccess(response: FetchMerchants.ResponseValue) {
retrieveMerchantsData(response.savingsWithAssociationsList)
}
override fun onError(message: String) {
_merchantUiState.value = MerchantUiState.Error(message)
}
})
}
fun retrieveMerchantsData(
savingsWithAssociationsList: List<SavingsWithAssociations>
) {
for (i in savingsWithAssociationsList.indices) {
mTaskLooper.addTask(
useCase = mUseCaseFactory.getUseCase(Constants.FETCH_CLIENT_DETAILS_USE_CASE)
as UseCase<FetchClientDetails.RequestValues, FetchClientDetails.ResponseValue>,
values = FetchClientDetails.RequestValues(
savingsWithAssociationsList[i].clientId.toLong()
),
taskData = TaskLooper.TaskData("Client data", i)
)
}
mTaskLooper.listen(object : TaskLooper.Listener {
override fun <R : UseCase.ResponseValue?> onTaskSuccess(
taskData: TaskLooper.TaskData,
response: R
) {
val responseValue = response as FetchClientDetails.ResponseValue
savingsWithAssociationsList[taskData.taskId].externalId =
responseValue.client.externalId
}
override fun onComplete() {
if (savingsWithAssociationsList.isEmpty()) {
_merchantUiState.value = MerchantUiState.Empty
} else {
_merchantUiState.value =
MerchantUiState.ShowMerchants(savingsWithAssociationsList)
}
}
override fun onFailure(message: String?) {
_merchantUiState.value = MerchantUiState.Error(message.toString())
}
})
}
}
sealed class MerchantUiState {
data object Loading : MerchantUiState()
data object Empty : MerchantUiState()
data class Error(val message: String) : MerchantUiState()
data class ShowMerchants(val merchants: List<SavingsWithAssociations>) : MerchantUiState()
}

View File

@ -0,0 +1,87 @@
package org.mifospay.feature.merchants
import androidx.compose.foundation.gestures.detectTapGestures
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.foundation.layout.size
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.mifospay.core.model.entity.accounts.savings.SavingsWithAssociations
import org.mifospay.R
import org.mifospay.core.designsystem.component.MifosCard
import org.mifospay.core.designsystem.theme.mifosText
import org.mifospay.core.designsystem.theme.styleMedium16sp
@Composable
fun MerchantsItem(
savingsWithAssociations: SavingsWithAssociations,
onMerchantClicked: () -> Unit,
onMerchantLongPressed: (String?) -> Unit
) {
MifosCard(
modifier = Modifier.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
onMerchantLongPressed(savingsWithAssociations.externalId)
}
)
},
onClick = { onMerchantClicked.invoke() },
colors = CardDefaults.cardColors(Color.White)
) {
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
) {
Icon(
painter = painterResource(id = R.drawable.ic_bank),
contentDescription = null,
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(start = 16.dp, end = 16.dp)
.size(39.dp)
)
Column {
Text(
text = savingsWithAssociations.clientName,
color = mifosText,
)
Text(
text = savingsWithAssociations.externalId,
modifier = Modifier.padding(top = 4.dp),
style = styleMedium16sp.copy(mifosText)
)
}
}
}
HorizontalDivider(
thickness = 1.dp,
modifier = Modifier.padding(8.dp)
)
}
}
@Preview(showBackground = true)
@Composable
private fun AccountsItemPreview() {
MerchantsItem(
savingsWithAssociations = SavingsWithAssociations(),
onMerchantClicked = {},
onMerchantLongPressed = {}
)
}

View File

@ -0,0 +1,17 @@
package org.mifospay.feature.merchants
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,6 +48,7 @@ include(":feature:invoices")
include(":feature:invoices")
include(":feature:settings")
include(":feature:profile")
include(":feature:merchants")
include(":feature:accounts")
include(":feature:standing-instruction")
include(":feature:payments")