Skip to content

Commit 11be9d5

Browse files
committed
feat(feature:autopay): add settings to edit bill, remove unused ui
1 parent dfc1287 commit 11be9d5

File tree

5 files changed

+226
-65
lines changed

5 files changed

+226
-65
lines changed

feature/autopay/src/commonMain/kotlin/org/mifospay/feature/autopay/AutoPayHistoryScreen.kt

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -47,44 +47,15 @@ fun AutoPayHistoryScreen(
4747
topBarTitle = "AutoPay History",
4848
backPress = onNavigateBack,
4949
) { paddingValues ->
50-
Column(
50+
LazyColumn(
5151
modifier = Modifier
5252
.fillMaxSize()
5353
.padding(paddingValues)
5454
.padding(16.dp),
55+
verticalArrangement = Arrangement.spacedBy(12.dp),
5556
) {
56-
Column(
57-
horizontalAlignment = Alignment.CenterHorizontally,
58-
modifier = Modifier.padding(bottom = 16.dp),
59-
) {
60-
Icon(
61-
imageVector = MifosIcons.History,
62-
contentDescription = null,
63-
modifier = Modifier.size(64.dp),
64-
tint = MaterialTheme.colorScheme.primary,
65-
)
66-
67-
Spacer(modifier = Modifier.height(16.dp))
68-
69-
Text(
70-
text = "AutoPay History",
71-
style = MaterialTheme.typography.headlineMedium,
72-
fontWeight = FontWeight.Medium,
73-
)
74-
75-
Text(
76-
text = "View your AutoPay transaction history and activities.",
77-
style = MaterialTheme.typography.bodyLarge,
78-
color = MaterialTheme.colorScheme.onSurfaceVariant,
79-
)
80-
}
81-
82-
LazyColumn(
83-
verticalArrangement = Arrangement.spacedBy(12.dp),
84-
) {
85-
items(getDummyHistoryItems()) { historyItem ->
86-
HistoryItemCard(historyItem = historyItem)
87-
}
57+
items(getDummyHistoryItems()) { historyItem ->
58+
HistoryItemCard(historyItem = historyItem)
8859
}
8960
}
9061
}
Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -83,66 +83,34 @@ fun AutoPayPreferencesScreen(
8383
.verticalScroll(rememberScrollState()),
8484
verticalArrangement = Arrangement.spacedBy(16.dp),
8585
) {
86-
Column(
87-
horizontalAlignment = Alignment.CenterHorizontally,
88-
) {
89-
Icon(
90-
imageVector = MifosIcons.Settings,
91-
contentDescription = null,
92-
modifier = Modifier.size(64.dp),
93-
tint = MaterialTheme.colorScheme.primary,
94-
)
95-
96-
Spacer(modifier = Modifier.height(16.dp))
97-
98-
Text(
99-
text = "AutoPay Settings",
100-
style = MaterialTheme.typography.headlineMedium,
101-
fontWeight = FontWeight.Medium,
102-
)
103-
104-
Text(
105-
text = "Configure global AutoPay settings and preferences.",
106-
style = MaterialTheme.typography.bodyLarge,
107-
color = MaterialTheme.colorScheme.onSurfaceVariant,
108-
)
109-
}
110-
111-
Spacer(modifier = Modifier.height(16.dp))
112-
113-
// General Settings
11486
GeneralSettingsSection(
11587
settings = state.globalSettings,
11688
onToggleAutoPay = { enabled ->
11789
viewModel.trySendAction(AutoPayPreferencesAction.ToggleAutoPayEnabled(enabled))
11890
},
11991
)
12092

121-
// Notification Settings
12293
NotificationSettingsSection(
12394
settings = state.globalSettings.notificationSettings,
12495
onSettingsChanged = { notificationSettings ->
12596
viewModel.trySendAction(AutoPayPreferencesAction.UpdateNotificationSettings(notificationSettings))
12697
},
12798
)
12899

129-
// Security Settings
130100
SecuritySettingsSection(
131101
settings = state.globalSettings.securitySettings,
132102
onSettingsChanged = { securitySettings ->
133103
viewModel.trySendAction(AutoPayPreferencesAction.UpdateSecuritySettings(securitySettings))
134104
},
135105
)
136106

137-
// AutoPay Rules
138107
AutoPayRulesSection(
139108
rules = state.globalSettings.globalAutoPayRules,
140109
onRulesChanged = { rules ->
141110
viewModel.trySendAction(AutoPayPreferencesAction.UpdateAutoPayRules(rules))
142111
},
143112
)
144113

145-
// Save Button
146114
if (state.hasUnsavedChanges) {
147115
Spacer(modifier = Modifier.height(16.dp))
148116

feature/autopay/src/commonMain/kotlin/org/mifospay/feature/autopay/EditBillScreen.kt

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,14 @@ import androidx.compose.foundation.layout.padding
2222
import androidx.compose.foundation.rememberScrollState
2323
import androidx.compose.foundation.text.KeyboardOptions
2424
import androidx.compose.foundation.verticalScroll
25+
import androidx.compose.material3.Card
26+
import androidx.compose.material3.CardDefaults
27+
import androidx.compose.material3.Checkbox
2528
import androidx.compose.material3.DatePicker
2629
import androidx.compose.material3.DatePickerDialog
30+
import androidx.compose.material3.DividerDefaults
2731
import androidx.compose.material3.ExperimentalMaterial3Api
32+
import androidx.compose.material3.HorizontalDivider
2833
import androidx.compose.material3.Icon
2934
import androidx.compose.material3.IconButton
3035
import androidx.compose.material3.IconButtonDefaults
@@ -39,6 +44,7 @@ import androidx.compose.runtime.remember
3944
import androidx.compose.runtime.setValue
4045
import androidx.compose.ui.Alignment
4146
import androidx.compose.ui.Modifier
47+
import androidx.compose.ui.text.font.FontWeight
4248
import androidx.compose.ui.text.input.ImeAction
4349
import androidx.compose.ui.text.input.KeyboardType
4450
import androidx.compose.ui.unit.dp
@@ -223,6 +229,28 @@ fun EditBillScreen(
223229
),
224230
)
225231

232+
AutoPaySection(
233+
enableAutoPay = state.formData.enableAutoPay,
234+
paymentMethod = state.formData.autoPayPaymentMethod,
235+
sourceAccount = state.formData.autoPaySourceAccount,
236+
maxAmount = state.formData.autoPayMaxAmount,
237+
paymentMethodError = state.validationResult.autoPayPaymentMethodError,
238+
sourceAccountError = state.validationResult.autoPaySourceAccountError,
239+
maxAmountError = state.validationResult.autoPayMaxAmountError,
240+
onEnableAutoPayChanged = { enabled ->
241+
viewModel.trySendAction(EditBillAction.UpdateAutoPayEnabled(enabled))
242+
},
243+
onPaymentMethodChanged = { paymentMethod ->
244+
viewModel.trySendAction(EditBillAction.UpdateAutoPayPaymentMethod(paymentMethod))
245+
},
246+
onSourceAccountChanged = { sourceAccount ->
247+
viewModel.trySendAction(EditBillAction.UpdateAutoPaySourceAccount(sourceAccount))
248+
},
249+
onMaxAmountChanged = { maxAmount ->
250+
viewModel.trySendAction(EditBillAction.UpdateAutoPayMaxAmount(maxAmount))
251+
},
252+
)
253+
226254
if (state.nextPaymentDates.isNotEmpty()) {
227255
Spacer(modifier = Modifier.height(16.dp))
228256

@@ -331,3 +359,110 @@ fun EditBillScreen(
331359
)
332360
}
333361
}
362+
363+
@Composable
364+
private fun AutoPaySection(
365+
enableAutoPay: Boolean,
366+
paymentMethod: String,
367+
sourceAccount: String,
368+
maxAmount: String,
369+
paymentMethodError: String?,
370+
sourceAccountError: String?,
371+
maxAmountError: String?,
372+
onEnableAutoPayChanged: (Boolean) -> Unit,
373+
onPaymentMethodChanged: (String) -> Unit,
374+
onSourceAccountChanged: (String) -> Unit,
375+
onMaxAmountChanged: (String) -> Unit,
376+
modifier: Modifier = Modifier,
377+
) {
378+
Card(
379+
modifier = modifier.fillMaxWidth(),
380+
colors = CardDefaults.cardColors(
381+
containerColor = KptTheme.colorScheme.surfaceContainerHigh,
382+
),
383+
) {
384+
Column(
385+
modifier = Modifier.padding(16.dp),
386+
verticalArrangement = Arrangement.spacedBy(12.dp),
387+
) {
388+
Row(
389+
modifier = Modifier.fillMaxWidth(),
390+
horizontalArrangement = Arrangement.SpaceBetween,
391+
verticalAlignment = Alignment.CenterVertically,
392+
) {
393+
Column {
394+
Text(
395+
text = "AutoPay Settings",
396+
style = KptTheme.typography.titleMedium,
397+
fontWeight = FontWeight.Medium,
398+
)
399+
Text(
400+
text = "Automatically pay this bill when due",
401+
style = KptTheme.typography.bodySmall,
402+
color = KptTheme.colorScheme.onSurfaceVariant,
403+
)
404+
}
405+
Checkbox(
406+
checked = enableAutoPay,
407+
onCheckedChange = onEnableAutoPayChanged,
408+
)
409+
}
410+
411+
if (enableAutoPay) {
412+
HorizontalDivider(Modifier, DividerDefaults.Thickness, DividerDefaults.color)
413+
414+
var showPaymentMethodDropdown by remember { mutableStateOf(false) }
415+
DropdownBox(
416+
expanded = showPaymentMethodDropdown,
417+
label = "Payment Method *",
418+
value = paymentMethod.ifBlank { "Select payment method" },
419+
readOnly = true,
420+
isError = paymentMethodError != null,
421+
errorText = paymentMethodError,
422+
onExpandChange = { showPaymentMethodDropdown = it },
423+
) {
424+
listOf("Bank Account", "Credit Card", "UPI").forEach { method ->
425+
DropdownBoxItem(
426+
text = method,
427+
onClick = {
428+
onPaymentMethodChanged(method)
429+
showPaymentMethodDropdown = false
430+
},
431+
)
432+
}
433+
}
434+
435+
MifosOutlinedTextField(
436+
label = "Source Account *",
437+
value = sourceAccount,
438+
onValueChange = onSourceAccountChanged,
439+
isError = sourceAccountError != null,
440+
errorMessage = sourceAccountError,
441+
singleLine = true,
442+
keyboardOptions = KeyboardOptions(
443+
imeAction = ImeAction.Next,
444+
),
445+
)
446+
447+
MifosOutlinedTextField(
448+
label = "Maximum Amount Limit (Optional)",
449+
value = maxAmount,
450+
onValueChange = onMaxAmountChanged,
451+
isError = maxAmountError != null,
452+
errorMessage = maxAmountError,
453+
singleLine = true,
454+
keyboardOptions = KeyboardOptions(
455+
keyboardType = KeyboardType.Decimal,
456+
imeAction = ImeAction.Done,
457+
),
458+
)
459+
460+
Text(
461+
text = "This amount will be used as a safety limit for automatic payments",
462+
style = KptTheme.typography.bodySmall,
463+
color = KptTheme.colorScheme.onSurfaceVariant,
464+
)
465+
}
466+
}
467+
}
468+
}

feature/autopay/src/commonMain/kotlin/org/mifospay/feature/autopay/EditBillViewModel.kt

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ class EditBillViewModel(
7878
is EditBillAction.SelectBiller -> {
7979
selectBiller(action.biller)
8080
}
81+
is EditBillAction.UpdateAutoPayEnabled -> {
82+
updateAutoPayEnabled(action.enabled)
83+
}
84+
is EditBillAction.UpdateAutoPayPaymentMethod -> {
85+
updateAutoPayPaymentMethod(action.paymentMethod)
86+
}
87+
is EditBillAction.UpdateAutoPaySourceAccount -> {
88+
updateAutoPaySourceAccount(action.sourceAccount)
89+
}
90+
is EditBillAction.UpdateAutoPayMaxAmount -> {
91+
updateAutoPayMaxAmount(action.maxAmount)
92+
}
8193
}
8294
}
8395

@@ -97,6 +109,10 @@ class EditBillViewModel(
97109
billerId = bill.billerId,
98110
billerName = bill.billerName,
99111
description = bill.description ?: "",
112+
enableAutoPay = bill.autoPayEnabled,
113+
autoPayPaymentMethod = bill.autoPayPaymentMethod ?: "",
114+
autoPaySourceAccount = bill.autoPaySourceAccount ?: "",
115+
autoPayMaxAmount = bill.autoPayMaxAmount?.toString() ?: "",
100116
)
101117
mutableStateFlow.update {
102118
it.copy(
@@ -246,6 +262,11 @@ class EditBillViewModel(
246262
billerId = formData.billerId,
247263
billerName = formData.billerName,
248264
description = formData.description.takeIf { it.isNotBlank() },
265+
// AutoPay configuration
266+
autoPayEnabled = formData.enableAutoPay,
267+
autoPayPaymentMethod = formData.autoPayPaymentMethod.takeIf { it.isNotBlank() },
268+
autoPaySourceAccount = formData.autoPaySourceAccount.takeIf { it.isNotBlank() },
269+
autoPayMaxAmount = formData.autoPayMaxAmount.toDoubleOrNull(),
249270
)
250271

251272
val result = billRepository.updateBill(bill)
@@ -322,6 +343,66 @@ class EditBillViewModel(
322343
}
323344
}
324345

346+
private fun updateAutoPayEnabled(enabled: Boolean) {
347+
mutableStateFlow.update {
348+
it.copy(
349+
formData = it.formData.copy(enableAutoPay = enabled),
350+
)
351+
}
352+
}
353+
354+
private fun updateAutoPayPaymentMethod(paymentMethod: String) {
355+
val paymentMethodError = if (stateFlow.value.formData.enableAutoPay && paymentMethod.isBlank()) {
356+
"Payment method is required when AutoPay is enabled"
357+
} else {
358+
null
359+
}
360+
361+
mutableStateFlow.update {
362+
it.copy(
363+
formData = it.formData.copy(autoPayPaymentMethod = paymentMethod),
364+
validationResult = it.validationResult.copy(autoPayPaymentMethodError = paymentMethodError),
365+
)
366+
}
367+
}
368+
369+
private fun updateAutoPaySourceAccount(sourceAccount: String) {
370+
val sourceAccountError = if (stateFlow.value.formData.enableAutoPay && sourceAccount.isBlank()) {
371+
"Source account is required when AutoPay is enabled"
372+
} else {
373+
null
374+
}
375+
376+
mutableStateFlow.update {
377+
it.copy(
378+
formData = it.formData.copy(autoPaySourceAccount = sourceAccount),
379+
validationResult = it.validationResult.copy(autoPaySourceAccountError = sourceAccountError),
380+
)
381+
}
382+
}
383+
384+
private fun updateAutoPayMaxAmount(maxAmount: String) {
385+
val maxAmountError = if (maxAmount.isNotBlank()) {
386+
val amount = maxAmount.toDoubleOrNull()
387+
if (amount == null) {
388+
"Invalid amount format"
389+
} else if (amount <= 0) {
390+
"Maximum amount must be greater than 0"
391+
} else {
392+
null
393+
}
394+
} else {
395+
null
396+
}
397+
398+
mutableStateFlow.update {
399+
it.copy(
400+
formData = it.formData.copy(autoPayMaxAmount = maxAmount),
401+
validationResult = it.validationResult.copy(autoPayMaxAmountError = maxAmountError),
402+
)
403+
}
404+
}
405+
325406
companion object {
326407
private const val KEY_STATE = "edit_bill_state"
327408
}
@@ -354,4 +435,10 @@ sealed interface EditBillAction {
354435
data object ClearValidationErrors : EditBillAction
355436
data object CalculateNextPaymentDates : EditBillAction
356437
data class SelectBiller(val biller: Biller) : EditBillAction
438+
439+
// AutoPay actions
440+
data class UpdateAutoPayEnabled(val enabled: Boolean) : EditBillAction
441+
data class UpdateAutoPayPaymentMethod(val paymentMethod: String) : EditBillAction
442+
data class UpdateAutoPaySourceAccount(val sourceAccount: String) : EditBillAction
443+
data class UpdateAutoPayMaxAmount(val maxAmount: String) : EditBillAction
357444
}

0 commit comments

Comments
 (0)