Skip to content

Conversation

@KadariPavani
Copy link

@KadariPavani KadariPavani commented Oct 3, 2025

Fixes - Jira-#560

Didn't create a Jira ticket, click here to create new.

Please Add Screenshots If there are any UI changes.
Description of Changes:
Implement Interest Charts step in the Fixed Deposit Account creation flow (Step 4) that allows users to:

  • View and manage chart details (name, dates, description)
  • View and modify interest rate charts
  • Download chart information
  • Navigate between setup steps

Please Add Screenshots If there are any UI changes.
Changes include:

  1. New Components:

    • InterestChartsPage.kt - Main UI for interest charts step
    • FixedDepositAccountCreationScreen.kt - Stepper-based creation flow
    • FixedDepositAccountState.kt - State management
    • FixedDepositAccountViewModel.kt - Business logic
  2. Features:

    • Chart information display
    • Interest rate modal with CRUD operations
    • Rate validation (0-100%)
    • Download functionality
    • Step navigation controls
    • Mobile responsive design
  3. Validation Rules:

    • Required chart information
    • Valid interest rate ranges
    • Confirmation for deletions
    • Non-empty chart requirement for progression

Didn't create a Jira ticket, click here to create new.

Please make sure these boxes are checked before submitting your pull request - thanks!

  • Run the static analysis check ./gradlew check or ci-prepush.sh to make sure you didn't break anything
  • If you have multiple commits please combine them into one commit by squashing them.

Please make sure these boxes are checked before submitting your pull request - thanks!

  • Run the static analysis check ./gradlew check or ci-prepush.sh to make sure you didn't break anything

  • If you have multiple commits please combine them into one commit by squashing them.

Summary by CodeRabbit

  • New Features
    • Added Rate Chart management to the Interest step of Fixed Deposit creation.
    • Users can open a Rate Chart modal to view rate entries and chart information.
    • Select an entry to open action choices (edit or delete) via dialogs.
    • Edit dialog includes validation; delete shows confirmation.
    • Back/Next navigation and a download placeholder for rate charts.
    • Sample in-memory data included for preview.

@revanthkumarJ
Copy link
Contributor

@KadariPavani can you upload the screenshots what you have updated

KadariPavani added a commit to KadariPavani/android-client that referenced this pull request Oct 27, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 27, 2025

Walkthrough

Adds two public data models (RateChartItem, ChartInformation) and a public composable InterestPage(onNext). Implements an in-page, modal-driven Rate Chart UI with listing, selection, action modal, edit dialog with validation, and delete confirmation using in-memory sample data and local state.

Changes

Cohort / File(s) Summary
Rate Chart UI & Models
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt
Added public data classes RateChartItem and ChartInformation; added public composable InterestPage(onNext: () -> Unit) and multiple private UI components (RateChartModal, RateChartItemCard, RateChartHeaderCard, ChartInformationCard, EditRateChartDialog, DeleteConfirmationDialog, ActionModal and helpers). Introduces in-memory sample data, modal visibility and selection state, edit/delete flows, validation, and placeholders for download/navigation.

Sequence Diagram

sequenceDiagram
    participant User
    participant InterestPage
    participant RateChartModal
    participant ActionModal
    participant EditDialog
    participant DeleteDialog

    User->>InterestPage: Open InterestPage / tap "View Rate Chart"
    InterestPage->>RateChartModal: show list (sample data)
    User->>RateChartModal: select RateChartItem
    RateChartModal->>ActionModal: present Edit / Delete / Download options

    alt Edit
        User->>ActionModal: Choose Edit
        ActionModal->>EditDialog: open with item
        User->>EditDialog: modify & confirm
        EditDialog->>InterestPage: update item state
    else Delete
        User->>ActionModal: Choose Delete
        ActionModal->>DeleteDialog: open confirmation
        User->>DeleteDialog: confirm
        DeleteDialog->>InterestPage: remove item
    else Download
        User->>ActionModal: Choose Download
        ActionModal->>InterestPage: trigger download placeholder
    end

    User->>InterestPage: Navigate Back / Next
    InterestPage->>User: navigate away
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review modal state transitions and selected-item lifecycle.
  • Validate EditRateChartDialog input validation and update logic.
  • Check DeleteConfirmationDialog removal and any list mutation edge cases.
  • Inspect public API additions (RateChartItem, ChartInformation, InterestPage) for naming/visibility and potential API surface impacts.

Poem

🐇 I found a chart in a cozy code glade,
Modals like burrows where choices are made,
I edit and delete with a hop and a cheer,
Data nibbling tidy, each change crystal clear,
A rabbit applauds the new Rate Chart parade 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Mifosac 560 new fixed deposits account interest charts step" directly aligns with the primary change in the pull request: implementing the Interest Charts step (Step 4) in the Fixed Deposit Account creation flow. The title references the specific Jira ticket (MIFOSAC-560), mentions "new fixed deposits account," and clearly identifies "interest charts step" as the focus. While the title doesn't enumerate all the details like Rate Chart modals, CRUD operations, or validation logic, this level of detail is not expected in a PR title. A developer scanning the git history would understand that this PR adds the interest charts functionality to the fixed deposit account creation process.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Nitpick comments (11)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt (4)

78-85: Make dialog state saveable across config changes.

Use rememberSaveable for transient UI state (dialogs, selection).

+// add import (top-level)
+import androidx.compose.runtime.saveable.rememberSaveable
@@
-    var showRateChartModal by remember { mutableStateOf(false) }
-    var showActionModal by remember { mutableStateOf(false) }
-    var showEditDialog by remember { mutableStateOf(false) }
-    var showDeleteConfirmation by remember { mutableStateOf(false) }
-    var selectedItemIndex by remember { mutableStateOf(-1) }
+    var showRateChartModal by rememberSaveable { mutableStateOf(false) }
+    var showActionModal by rememberSaveable { mutableStateOf(false) }
+    var showEditDialog by rememberSaveable { mutableStateOf(false) }
+    var showDeleteConfirmation by rememberSaveable { mutableStateOf(false) }
+    var selectedItemIndex by rememberSaveable { mutableStateOf(-1) }

124-136: Wire the Back action; expose onBack with a safe default.

Back button is a no‑op. Add onBack param and invoke it.

-fun InterestPage(onNext: () -> Unit) {
+fun InterestPage(onNext: () -> Unit, onBack: () -> Unit = {}) {
@@
-            OutlinedButton(
-                onClick = { /* Handle back - will be managed by stepper */ },
+            OutlinedButton(
+                onClick = onBack,
                 modifier = Modifier.weight(1f),
                 shape = RoundedCornerShape(8.dp)
             ) {
                 Text("Back")
             }

Also applies to: 79-80


397-401: Use proper icons instead of raw glyphs for arrows/indicators.

Replace "▲" and "›" text with material icons for better a11y and theme consistency.

+// add imports (top-level)
+import androidx.compose.material.icons.filled.ArrowUpward
+import androidx.compose.material.icons.filled.KeyboardArrowRight
@@
-                            Text(
-                                text = "▲",
-                                fontSize = 10.sp,
-                                color = Color(0xFF4CAF50)
-                            )
+                            Icon(
+                                imageVector = Icons.Filled.ArrowUpward,
+                                contentDescription = null
+                            )
@@
-                Text(
-                    text = "›",
-                    fontSize = 24.sp,
-                    color = Color(0xFF9E9E9E),
-                    modifier = Modifier.padding(start = 2.dp)
-                )
+                Icon(
+                    imageVector = Icons.Filled.KeyboardArrowRight,
+                    contentDescription = null,
+                    tint = MaterialTheme.colorScheme.onSurfaceVariant
+                )
@@
-                            Text(
-                                text = "›",
-                                fontSize = 24.sp,
-                                color = Color(0xFF9E9E9E),
-                                modifier = Modifier.padding(start = 2.dp)
-                            )
+                            Icon(
+                                imageVector = Icons.Filled.KeyboardArrowRight,
+                                contentDescription = null,
+                                tint = MaterialTheme.colorScheme.onSurfaceVariant
+                            )

Also applies to: 524-530, 693-697


148-163: Download actions are stubs; thread through callbacks to ViewModel.

onDownload handlers are TODOs. Expose onDownloadAll/onDownloadItem and delegate to VM/service (export to CSV/PDF or share intent).

Minimal signature change:

-private fun RateChartModal(
-    rateChartItems: List<RateChartItem>,
-    onDismiss: () -> Unit,
-    onItemClick: (Int) -> Unit,
-    onDownload: () -> Unit
+private fun RateChartModal(
+    rateChartItems: List<RateChartItem>,
+    onDismiss: () -> Unit,
+    onItemClick: (Int) -> Unit,
+    onDownloadAll: () -> Unit
 )
@@
-                    Button(
-                        onClick = onDownload,
+                    Button(
+                        onClick = onDownloadAll,

Then add onDownloadItem: (Int) -> Unit to ActionModal and call it from the item’s "Download" action.

Also applies to: 181-183

feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountViewModel.kt (1)

86-89: Download action shows success without implementation.

Emit a neutral info or disable until implemented to avoid misleading UX.

-// TODO: Implement chart download
-sendEvent(FixedDepositAccountEvent.ShowDownloadSuccess)
+sendEvent(FixedDepositAccountEvent.ShowError("Download not implemented yet"))

Or gate the “Download” button behind actual availability.

feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/pages/InterestChartsPage.kt (2)

70-92: Externalize hardcoded strings and use theme colors for accessibility.

Replace inline strings (“Chart Name”, “Back”, “Next”, “Download”, etc.) with string resources; avoid Color.Green and prefer MaterialTheme.colorScheme.*.

Example:

-Text("View Interest Rate Chart")
+Text(stringResource(Res.string.feature_fixed_deposit_view_interest_chart))

-Text("Back")
+Text(stringResource(Res.string.common_back))

-Text("Next")
+Text(stringResource(Res.string.common_next))

-Text("Interest Rate: ${chart.interestRate}%")
+Text(stringResource(Res.string.feature_fixed_deposit_interest_rate_fmt, chart.interestRate))
- color = Color.Green
+ color = MaterialTheme.colorScheme.primary

-Text("Download")
+Text(stringResource(Res.string.common_download))
-Text("Close")
+Text(stringResource(Res.string.common_close))
-Text("Confirm Delete")
+Text(stringResource(Res.string.common_confirm_delete))
-Text("Are you sure you want to delete this interest rate entry?")
+Text(stringResource(Res.string.feature_fixed_deposit_delete_rate_confirm))
-Text("Delete")
+Text(stringResource(Res.string.common_delete))
-Text("Cancel")
+Text(stringResource(Res.string.common_cancel))

Add missing keys in composeResources accordingly.

Also applies to: 96-101, 112-124, 127-165, 170-190, 237-249


127-149: Large list inside AlertDialog text: constrain height to avoid overflow.

LazyColumn in AlertDialog body can overflow on small screens. Wrap in a Box with a max height or use DialogProperties(usePlatformDefaultWidth = false) with a sized container.

- text = {
-     LazyColumn {
+ text = {
+     Box(Modifier.heightIn(max = DesignToken.sizes.dialogBodyMax)) {
+         LazyColumn {
              items(state.interestRateCharts) { chart -> /* ... */ }
-     }
+         }
+     }
   }
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountState.kt (2)

18-18: State imports model from a UI package; decouple layers.

Importing InterestRateChart from ...pages ties state/domain to UI. Move the model to a non-UI package/file and import from there.

-import com.mifos.feature.client.fixedDepositAccount.creation.pages.InterestRateChart
+import com.mifos.feature.client.fixedDepositAccount.creation.model.InterestRateChart

Create creation/model/InterestRateChart.kt with the data class.

Also applies to: 30-36


30-36: Dates as String: consider kotlinx-datetime for correctness.

fromDate/endDate as String make validation/formatting hard. Prefer LocalDate from kotlinx-datetime and format in UI.

feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationScreen.kt (2)

41-52: Handle navigation events to move the Stepper.

Update step when receiving NavigateToStep.

 is FixedDepositAccountEvent.NavigateToStep -> {
-    // Handle step navigation
+    viewModel.trySendAction(FixedDepositAccountAction.SetStep(event.step))
 }

33-38: Accept and use initial args from route.

Add initialClientId and initialStep parameters (see Route comment), and initialize state once.

-fun FixedDepositAccountCreationScreen(
+fun FixedDepositAccountCreationScreen(
     navController: NavController,
     navigateBack: () -> Unit,
     modifier: Modifier = Modifier,
-    viewModel: FixedDepositAccountViewModel = koinViewModel()
+    viewModel: FixedDepositAccountViewModel = koinViewModel(),
+    initialClientId: Int = -1,
+    initialStep: Int = 3,
 ) {
     val state by viewModel.stateFlow.collectAsStateWithLifecycle()
+    LaunchedEffect(initialClientId, initialStep) {
+        viewModel.trySendAction(FixedDepositAccountAction.SetStep(initialStep.coerceIn(0, 5)))
+        // Optionally store clientId in state if needed
+    }

Also applies to: 55-63

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37df893 and 20d4797.

📒 Files selected for processing (7)
  • feature/client/src/commonMain/composeResources/values/strings.xml (1 hunks)
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationRoute.kt (1 hunks)
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationScreen.kt (1 hunks)
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountState.kt (1 hunks)
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountViewModel.kt (1 hunks)
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/pages/InterestChartsPage.kt (1 hunks)
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt (1)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/pages/InterestChartsPage.kt (1)
  • RateChartItem (194-253)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationScreen.kt (3)
core/ui/src/commonMain/kotlin/com/mifos/core/ui/util/EventsEffect.kt (1)
  • EventsEffect (28-43)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/pages/InterestChartsPage.kt (1)
  • InterestChartsPage (39-192)
core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosStepper.kt (1)
  • MifosStepper (47-137)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationRoute.kt (1)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationScreen.kt (1)
  • FixedDepositAccountCreationScreen (32-116)
🔇 Additional comments (4)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt (1)

137-144: Good guard: disallow Next when no chart items.

Enabling Next only when rateChartItems.isNotEmpty() aligns with “Non-empty chart requirement.”

Confirm the stepper also enforces this rule on navigation to prevent backdoor progression via programmatic calls.

feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationRoute.kt (2)

41-51: LGTM for type-safe navigation helper.

Helper reads well and keeps callers concise.


23-27: Route step parameter is not used in screen implementation.

The step parameter in FixedDepositAccountCreationRoute is defined with a default of 0, but it's never extracted or used by FixedDepositAccountCreationScreen. The screen relies entirely on state.currentStep from the ViewModel (which defaults to 4), not the route parameter. Validation of the route's step parameter is unnecessary since deep-linked values would be ignored anyway. If step navigation is intended to be routable, the screen implementation needs to extract and use the route parameter first.

Likely an incorrect or invalid review comment.

feature/client/src/commonMain/composeResources/values/strings.xml (1)

31-39: Well‑organized localization strings for Fixed Deposit Account creation.

The new string resources follow established naming conventions, are properly organized with a comment header, and align with the multi‑step account creation flow described in the PR (Product → Details → Terms → Interest Charts → Charges → Preview, plus Chart Information).

Comment on lines 33 to 38
composable<FixedDepositAccountCreationRoute> {
FixedDepositAccountCreationScreen(
navController = navController,
navigateBack = navigateBack
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Route args are ignored; wire clientId/step into screen/VM.

Currently you don’t read FixedDepositAccountCreationRoute inside the destination, so clientId and step are dropped. Parse the typed route and pass values onward (screen or VM).

Apply:

-    composable<FixedDepositAccountCreationRoute> {
-        FixedDepositAccountCreationScreen(
+    composable<FixedDepositAccountCreationRoute> { backStackEntry ->
+        val route = backStackEntry.toRoute<FixedDepositAccountCreationRoute>()
+        FixedDepositAccountCreationScreen(
             navController = navController,
-            navigateBack = navigateBack
+            navigateBack = navigateBack,
+            initialClientId = route.clientId,
+            initialStep = route.step
         )
     }

Add matching parameters to FixedDepositAccountCreationScreen and forward to the VM (via Koin parameters or an init action).

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationRoute.kt
around lines 33 to 38, the composable is ignoring the typed
FixedDepositAccountCreationRoute so clientId and step are dropped; update the
destination to read/parse the route instance, extract clientId and step, add
matching parameters to FixedDepositAccountCreationScreen signature, and forward
those values into the screen's ViewModel either by supplying Koin parameters
when resolving the VM or by dispatching an init action from the screen to the
VM.

Comment on lines 64 to 104
val steps = listOf(
Step(
title = stringResource(Res.string.feature_fixed_deposit_step_product),
content = {
// Product Selection Page
}
),
Step(
title = stringResource(Res.string.feature_fixed_deposit_step_details),
content = {
// Details Page
}
),
Step(
title = stringResource(Res.string.feature_fixed_deposit_step_terms),
content = {
// Terms Page
}
),
Step(
title = stringResource(Res.string.feature_fixed_deposit_step_interest),
content = {
InterestChartsPage(
state = state,
onAction = remember(viewModel) { { viewModel.trySendAction(it) } }
)
}
),
Step(
title = stringResource(Res.string.feature_fixed_deposit_step_charges),
content = {
// Charges Page
}
),
Step(
title = stringResource(Res.string.feature_fixed_deposit_step_preview),
content = {
// Preview Page
}
)
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Step API mismatch: use name = instead of title =.

MifosStepper renders step.name; passing title = won’t compile if Step doesn’t define that parameter.

Apply:

- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_product),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_product),
      content = { /* ... */ }
 ),
- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_details),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_details),
      content = { /* ... */ }
 ),
- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_terms),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_terms),
      content = { /* ... */ }
 ),
- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_interest),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_interest),
      content = { /* ... */ }
 ),
- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_charges),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_charges),
      content = { /* ... */ }
 ),
- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_preview),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_preview),
      content = { /* ... */ }
 )

🏁 Script executed:

#!/bin/bash
# Confirm Step's API
rg -nP --type=kotlin "data\\s+class\\s+Step\\s*\\(" core/ui | sed -n '1,120p'

Length of output: 94



---

🏁 Script executed:

```shell
#!/bin/bash
# Extract full Step data class definition
cat core/ui/src/commonMain/kotlin/com/mifos/core/ui/components/MifosStepper.kt | sed -n '42,60p'

Length of output: 408



---

🏁 Script executed:

```shell
#!/bin/bash
# Verify the actual code in FixedDepositAccountCreationScreen.kt uses title =
cat feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationScreen.kt | sed -n '64,104p'

Length of output: 1575


Change title = to name = in all Step instantiations (lines 66, 71, 76, 81, 89, 97).

Step's constructor parameter is name, not title. The current code will not compile.

- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_product),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_product),
      content = { /* ... */ }
 ),
- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_details),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_details),
      content = { /* ... */ }
 ),
- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_terms),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_terms),
      content = { /* ... */ }
 ),
- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_interest),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_interest),
      content = { /* ... */ }
 ),
- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_charges),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_charges),
      content = { /* ... */ }
 ),
- Step(
-     title = stringResource(Res.string.feature_fixed_deposit_step_preview),
+ Step(
+     name = stringResource(Res.string.feature_fixed_deposit_step_preview),
      content = { /* ... */ }
 )
🤖 Prompt for AI Agents
In
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationScreen.kt
around lines 64 to 104, the Step instances use the wrong constructor parameter
`title`; change each `title =` to `name =` for all Step(...) calls (lines ~66,
71, 76, 81, 89, 97) so the code matches the Step constructor and compiles; keep
the stringResource(...) expressions as-is and only rename the parameter.

Comment on lines 109 to 113
onStepChange = { newStep ->
// Handle step change here
},
modifier = Modifier.fillMaxWidth()
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Wire onStepChange to update state.

Currently a no-op; hook into the VM.

- onStepChange = { newStep ->
-     // Handle step change here
- },
+ onStepChange = { newStep ->
+     viewModel.trySendAction(FixedDepositAccountAction.SetStep(newStep))
+ },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onStepChange = { newStep ->
// Handle step change here
},
modifier = Modifier.fillMaxWidth()
)
onStepChange = { newStep ->
viewModel.trySendAction(FixedDepositAccountAction.SetStep(newStep))
},
modifier = Modifier.fillMaxWidth()
)
🤖 Prompt for AI Agents
In
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountCreationScreen.kt
around lines 109 to 113, the onStepChange lambda is currently a no-op; hook it
up to the screen's ViewModel so UI step changes update state. Replace the empty
lambda with a call into the VM (e.g., viewModel.onStepChange(newStep) or
viewModel.setCurrentStep(newStep)); if the ViewModel lacks a suitable method,
add one that updates its step/state and exposes it via State/Flow so the UI
re-composes accordingly. Ensure the call runs on the main thread if needed and
remove the placeholder comment.

Comment on lines 20 to 28
data class FixedDepositAccountState(
val clientId: Int = -1,
val currentStep: Int = 4,
val template: FixedDepositTemplate? = null,
val interestRateCharts: List<InterestRateChart> = emptyList(),
val selectedChart: InterestRateChart? = null,
val error: String? = null,
val isLoading: Boolean = false
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

currentStep default points to the wrong page.

With 6 steps and zero-based indexing, Interest step index is 3, but default is 4 (Charges). Set to the intended starting step or read from route.

-    val currentStep: Int = 4,
+    val currentStep: Int = 3,

Also consider initializing from the route (see Route comment).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data class FixedDepositAccountState(
val clientId: Int = -1,
val currentStep: Int = 4,
val template: FixedDepositTemplate? = null,
val interestRateCharts: List<InterestRateChart> = emptyList(),
val selectedChart: InterestRateChart? = null,
val error: String? = null,
val isLoading: Boolean = false
)
data class FixedDepositAccountState(
val clientId: Int = -1,
val currentStep: Int = 3,
val template: FixedDepositTemplate? = null,
val interestRateCharts: List<InterestRateChart> = emptyList(),
val selectedChart: InterestRateChart? = null,
val error: String? = null,
val isLoading: Boolean = false
)
🤖 Prompt for AI Agents
In
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountState
around lines 20 to 28, the default currentStep is set to 4 which points to the
Charges page; change the default to 3 (the Interest step, using zero-based
indexing) or initialize currentStep from the routing entry instead of a
hardcoded value; update the constructor default to currentStep: Int = 3 or
replace the default with a call that reads the initial step from the route so
the state starts on the intended page.

Comment on lines 38 to 57
sealed class FixedDepositAccountAction {
object NavigateBack : FixedDepositAccountAction()
object NavigateNext : FixedDepositAccountAction()
object DownloadChart : FixedDepositAccountAction()
data class EditInterestRate(val chart: InterestRateChart) : FixedDepositAccountAction()
data class DeleteInterestRate(val chartId: String) : FixedDepositAccountAction()
data class AddInterestRate(
val amountRange: String,
val period: String,
val interestRate: Double,
val description: String?
) : FixedDepositAccountAction()
data class UpdateInterestRate(
val chartId: String,
val amountRange: String,
val period: String,
val interestRate: Double,
val description: String?
) : FixedDepositAccountAction()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add a SetStep action to support direct step changes (Stepper + events).

To make Stepper interactive and handle NavigateToStep intents, add:

 sealed class FixedDepositAccountAction {
+    data class SetStep(val step: Int) : FixedDepositAccountAction()
     object NavigateBack : FixedDepositAccountAction()
     object NavigateNext : FixedDepositAccountAction()
     ...
 }

Handle it in the ViewModel (see corresponding comment).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sealed class FixedDepositAccountAction {
object NavigateBack : FixedDepositAccountAction()
object NavigateNext : FixedDepositAccountAction()
object DownloadChart : FixedDepositAccountAction()
data class EditInterestRate(val chart: InterestRateChart) : FixedDepositAccountAction()
data class DeleteInterestRate(val chartId: String) : FixedDepositAccountAction()
data class AddInterestRate(
val amountRange: String,
val period: String,
val interestRate: Double,
val description: String?
) : FixedDepositAccountAction()
data class UpdateInterestRate(
val chartId: String,
val amountRange: String,
val period: String,
val interestRate: Double,
val description: String?
) : FixedDepositAccountAction()
}
sealed class FixedDepositAccountAction {
data class SetStep(val step: Int) : FixedDepositAccountAction()
object NavigateBack : FixedDepositAccountAction()
object NavigateNext : FixedDepositAccountAction()
object DownloadChart : FixedDepositAccountAction()
data class EditInterestRate(val chart: InterestRateChart) : FixedDepositAccountAction()
data class DeleteInterestRate(val chartId: String) : FixedDepositAccountAction()
data class AddInterestRate(
val amountRange: String,
val period: String,
val interestRate: Double,
val description: String?
) : FixedDepositAccountAction()
data class UpdateInterestRate(
val chartId: String,
val amountRange: String,
val period: String,
val interestRate: Double,
val description: String?
) : FixedDepositAccountAction()
}

Comment on lines 274 to 280
data class InterestRateChart(
val id: String,
val amountRange: String,
val period: String,
val interestRate: Double,
val description: String? = null
) No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Move InterestRateChart out of UI file to a shared model.

State/VM import this UI-local class, creating an undesirable UI→domain coupling.

  • Create creation/model/InterestRateChart.kt (same package ...creation) and move the data class there.
  • Update imports in state and VM to that model.

🏁 Script executed:

#!/bin/bash
rg -nP --type=kotlin "\bInterestRateChart\b" -C2

Length of output: 7570


🏁 Script executed:

#!/bin/bash
# Verify all usages of InterestRateChart across the entire codebase
rg -n "InterestRateChart" --type=kotlin -g '!test' -g '!Test'

Length of output: 1797


🏁 Script executed:

#!/bin/bash
# Check if model directory already exists in the creation package
fd -t d "model" feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Verify the file structure of the creation package
fd -t f --max-depth 3 . feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/ | head -20

Length of output: 615


Move InterestRateChart to a dedicated model package to eliminate UI→domain coupling.

The InterestRateChart data class is currently defined in pages/InterestChartsPage.kt but imported by both FixedDepositAccountState.kt and FixedDepositAccountViewModel.kt, creating improper layering between UI and business logic.

Refactor required:

  • Create creation/model/InterestRateChart.kt and move the data class there
  • Update imports in FixedDepositAccountState.kt (line 18) and FixedDepositAccountViewModel.kt (line 20) to reference the new model package
  • No other usages exist beyond these two files
🤖 Prompt for AI Agents
In
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/pages/InterestChartsPage.kt
lines 274-280 the InterestRateChart data class is declared in the UI page
causing UI→domain coupling; move that data class into a new model file at
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/model/InterestRateChart.kt
containing the same data class definition, remove it from InterestChartsPage.kt,
and update the import in
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountState.kt
(line 18) and in
feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/FixedDepositAccountViewModel.kt
(line 20) to import
com.mifos.feature.client.fixedDepositAccount.creation.model.InterestRateChart
instead; verify no other usages remain and run a build to confirm imports
resolve.

Comment on lines 61 to 77
// Data class for Rate Chart Item
data class RateChartItem(
val amountRange: String,
val description: String,
val period: String,
val interestRate: String
)

// Data class for Chart Information
data class ChartInformation(
val name: String,
val validFromDate: String,
val endDate: String,
val description: String,
val groupingByAmount: Boolean
)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Unify rate‑chart models; avoid duplication and stringly‑typed fields.

This file introduces RateChartItem and ChartInformation that overlap with the creation flow’s InterestRateChart model. Duplicating models risks drift and mapping bugs. Prefer reusing the existing model (or elevate a shared domain/UI model) and keep percent/currency as numeric types with UI formatting only.

Run to locate existing definitions/usages and assess consolidation:


🏁 Script executed:

#!/bin/bash
rg -nC2 -g '!**/build/**' -P '(data\s+class\s+InterestRateChart\b|class\s+.*Interest.*Chart\b|RateChartItem\b)'

Length of output: 8417



---

🏁 Script executed:

```shell
#!/bin/bash
# Extract full InterestRateChart definition from InterestChartsPage.kt
sed -n '274,290p' feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/pages/InterestChartsPage.kt

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Extract full RateChartItem and ChartInformation definitions from InterestPage.kt
sed -n '61,76p' feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt

Length of output: 381


🏁 Script executed:

#!/bin/bash
# Check imports and model reachability in InterestPage.kt
head -30 feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt | grep -i import

Length of output: 942


🏁 Script executed:

#!/bin/bash
# Verify if InterestRateChart is exported or accessible from InterestChartsPage module
rg -n "export|public" feature/client/src/commonMain/kotlin/com/mifos/feature/client/fixedDepositAccount/creation/pages/InterestChartsPage.kt | head -5

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check the module structure: is there a shared models file or package?
fd -type f -name "*.kt" feature/client/src/commonMain/kotlin/com/mifos/feature/client/ | grep -i model | head -10

Length of output: 170


🏁 Script executed:

#!/bin/bash
# Verify if RateChartItem is used outside InterestPage.kt (risk of duplication drift)
rg -l "RateChartItem" --type kotlin feature/client/src/

Length of output: 225


🏁 Script executed:

#!/bin/bash
# Verify if InterestRateChart is used outside InterestChartsPage/FixedDepositAccountState
rg -l "InterestRateChart" --type kotlin feature/client/src/

Length of output: 360


🏁 Script executed:

#!/bin/bash
# Check if there is any mapping logic between models in the codebase
rg -n "InterestRateChart|RateChartItem" -A 2 -B 2 feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt | grep -E "(toInterest|toRate|map|convert)" || echo "No mapping found"

Length of output: 16


🏁 Script executed:

#!/bin/bash
# Look at the navigation/usage between the two screens to understand data flow
rg -n "InterestPage|InterestChartsPage" feature/client/src/commonMain/kotlin/com/mifos/feature/client/ --type kotlin | grep -E "(Navigation|Route|navigate)" | head -10

Length of output: 0



---

🏁 Script executed:

```shell
#!/bin/bash
# Check file context: when was InterestPage.kt added, is it preview or integration planned?
fd -name InterestPage.kt feature/client/src/ -exec ls -lh {} \;

Length of output: 230


🏁 Script executed:

#!/bin/bash
# Look for validation rules on interestRate in both files
rg -n "interestRate|rate.*valid" feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt -A 1 -B 1 | head -20

Length of output: 729


🏁 Script executed:

#!/bin/bash
# Check if amountRange is parsed/formatted anywhere in InterestPage.kt
rg -n "amountRange|amount.*Range|toDouble|toInt|parse" feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt | head -15

Length of output: 557


🏁 Script executed:

#!/bin/bash
# Verify structure: are these flows connected or separate products?
rg -n "newFixedDepositAccount|fixedDepositAccount" feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt | head -5

Length of output: 64


🏁 Script executed:

#!/bin/bash
# Look for the key validation constraint mentioned in review (rate 0-100)
rg -n "rate.*>.*0.*<.*100|rate.*range|percent" feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Check ChartInformation usage in InterestPage.kt
rg -n "ChartInformation" feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt

Length of output: 169


🏁 Script executed:

#!/bin/bash
# Determine if RateChartItem fields are ever converted to numeric
rg -nP "(amountRange|interestRate).*=.*\(" feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search across repo for any shared domain/UI model layers
fd -name "*.kt" feature/client/src/commonMain/kotlin/com/mifos/feature/client/ -path "*/data/*" -o -path "*/domain/*" -o -path "*/model/*" | head -20

Length of output: 230


Eliminate stringly-typed numeric fields; fix validation bounds mismatch.

The models RateChartItem and ChartInformation are scoped to this new flow and don't duplicate the creation flow's InterestRateChart (separate packages). However, stringly-typed fields are the real problem:

  • interestRate (line 66): stored as String ("7%"), parsed to Double only at validation (line 903). Use Double with UI-only formatting.
  • amountRange (line 63): stored as String; no validation or bounds. Clarify: numeric range (Double..Double) or display string?
  • Validation bounds mismatch (line 904): code enforces rate > 0 && rate < 100 (excludes 0 and 100), but error message (line 890) says "0-100" (inclusive). Fix either validation or message.

Suggested structure:

data class RateChartItem(
    val amountRangeMin: Double,
    val amountRangeMax: Double,
    val description: String,
    val period: String,
    val interestRate: Double  // numeric, formatted in UI
)
🤖 Prompt for AI Agents
In
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt
around lines 61 to 77, replace stringly-typed numeric fields with numeric types
and fix the bounds mismatch: change interestRate from String to Double (keep
formatting to a percent in the UI layer only), replace amountRange:String with
explicit amountRangeMin:Double and amountRangeMax:Double (or a clear numeric
range type) and update all places that parsed those strings to use the numeric
fields directly; also reconcile the validation and message by either making the
validation inclusive (rate >= 0 && rate <= 100) to match the "0-100" message or
change the message to reflect exclusive bounds, and update any
parsing/validation callsites to handle null/NaN safely and show formatted values
in the UI.

Comment on lines 210 to 215
onConfirm = {
rateChartItems.removeAt(selectedItemIndex)
showDeleteConfirmation = false
showRateChartModal = true
}
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Reset selection after delete to avoid stale index.

After removal, selectedItemIndex can point at a removed element. Reset it.

             onConfirm = {
                 rateChartItems.removeAt(selectedItemIndex)
+                selectedItemIndex = -1
                 showDeleteConfirmation = false
                 showRateChartModal = true
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onConfirm = {
rateChartItems.removeAt(selectedItemIndex)
showDeleteConfirmation = false
showRateChartModal = true
}
)
onConfirm = {
rateChartItems.removeAt(selectedItemIndex)
selectedItemIndex = -1
showDeleteConfirmation = false
showRateChartModal = true
}
)
🤖 Prompt for AI Agents
In
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt
around lines 210 to 215, after removing the item from rateChartItems you must
reset the selection to avoid selectedItemIndex pointing at a removed element;
update the onConfirm flow to set selectedItemIndex to an appropriate sentinel
(e.g., -1) or clamp it within the new list bounds after calling removeAt, and
then proceed to close the confirmation and open the modal as before.

Comment on lines 219 to 345
@Composable
private fun ChartInformationCard(
chartInfo: ChartInformation,
onViewClick: () -> Unit
) {
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(12.dp),
border = BorderStroke(1.dp, Color(0xFFE0E0E0)),
colors = CardDefaults.cardColors(
containerColor = Color.White
),
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
InfoRow(label = "Name:", value = chartInfo.name)
InfoRow(label = "Valid from Date:", value = chartInfo.validFromDate)
InfoRow(label = "End Date:", value = chartInfo.endDate)
InfoRow(label = "Description:", value = chartInfo.description)
InfoRow(
label = "Grouping by Amount:",
value = if (chartInfo.groupingByAmount) "Yes" else "No"
)

Spacer(modifier = Modifier.height(4.dp))

Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "INTEREST RATE CHART",
style = MaterialTheme.typography.labelLarge,
fontWeight = FontWeight.SemiBold,
fontSize = 13.sp,
color = Color(0xFF212121)
)

TextButton(
onClick = onViewClick,
shape = RoundedCornerShape(4.dp)
) {
Text(
text = "View",
color = MaterialTheme.colorScheme.primary,
fontSize = 14.sp,
fontWeight = FontWeight.Medium
)
}
}
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace hard‑coded strings/colors with resources and theme tokens (i18n + dark mode).

Literal text and colors break localization and dark theme. Use stringResource(Res.string.*) and MaterialTheme.colorScheme/typography (or DesignToken) consistently. Apply across this file.

Example edits (apply pattern broadly):

-                    text = "INTEREST RATE CHART",
+                    text = stringResource(Res.string.interest_rate_chart_title),
@@
-                TextButton(
-                    onClick = onViewClick,
+                TextButton(
+                    onClick = onViewClick,
                     shape = RoundedCornerShape(4.dp)
                 ) {
-                    Text(
-                        text = "View",
-                        color = MaterialTheme.colorScheme.primary,
-                        fontSize = 14.sp,
-                        fontWeight = FontWeight.Medium
-                    )
+                    Text(stringResource(Res.string.view))
                 }

And for colors:

-        colors = CardDefaults.cardColors(containerColor = Color.White),
+        colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
@@
-                    color = Color(0xFF212121)
+                    color = MaterialTheme.colorScheme.onSurface

If keys don’t exist yet (e.g., view, back, next, download, rate_chart_title, amount_range, description, period, interest_rate, delete, cancel), please add them to shared resources. I can help generate them.

Also applies to: 306-459

Comment on lines 821 to 877
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun EditRateChartDialog(
item: RateChartItem,
onDismiss: () -> Unit,
onConfirm: (RateChartItem) -> Unit
) {
var amountRange by remember { mutableStateOf(item.amountRange) }
var description by remember { mutableStateOf(item.description) }
var period by remember { mutableStateOf(item.period) }
var interestRate by remember { mutableStateOf(item.interestRate.replace("%", "")) }
var interestRateError by remember { mutableStateOf(false) }

AlertDialog(
onDismissRequest = onDismiss,
shape = RoundedCornerShape(16.dp),
containerColor = Color.White,
title = {
Text(
text = "Edit Interest Rate",
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
fontSize = 20.sp,
color = Color(0xFF212121)
)
},
text = {
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
OutlinedTextField(
value = amountRange,
onValueChange = { amountRange = it },
label = { Text("Amount Range") },
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp)
)

OutlinedTextField(
value = description,
onValueChange = { description = it },
label = { Text("Description") },
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp)
)

OutlinedTextField(
value = period,
onValueChange = { period = it },
label = { Text("Period") },
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp)
)

OutlinedTextField(
value = interestRate,
onValueChange = {
interestRate = it
val rate = it.toDoubleOrNull()
interestRateError = rate == null || rate <= 0 || rate >= 100
},
label = { Text("Interest Rate (%)") },
isError = interestRateError,
supportingText = {
if (interestRateError) {
Text(
"Please enter a valid percentage (0-100)",
color = Color(0xFFD32F2F)
)
}
},
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp)
)
}
},
confirmButton = {
Button(
onClick = {
val rate = interestRate.toDoubleOrNull()
if (rate != null && rate > 0 && rate < 100) {
onConfirm(
RateChartItem(
amountRange = amountRange,
description = description,
period = period,
interestRate = "$interestRate%"
)
)
}
},
enabled = !interestRateError && interestRate.isNotEmpty(),
shape = RoundedCornerShape(8.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFF5C6BC0)
)
) {
Text("Save", fontWeight = FontWeight.Medium)
}
},
dismissButton = {
TextButton(
onClick = onDismiss,
shape = RoundedCornerShape(8.dp)
) {
Text("Cancel", color = Color(0xFF757575))
}
}
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix validation to match 0–100% requirement and force numeric keyboard.

Bounds are exclusive; PR says 0–100%. Also prefer numeric keyboard and trimmed parsing.

+// add imports (top-level)
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.ui.text.input.KeyboardType
@@
-    var interestRate by remember { mutableStateOf(item.interestRate.replace("%", "")) }
+    var interestRate by remember { mutableStateOf(item.interestRate.replace("%", "")) }
     var interestRateError by remember { mutableStateOf(false) }
@@
-                OutlinedTextField(
+                OutlinedTextField(
                     value = interestRate,
                     onValueChange = {
-                        interestRate = it
-                        val rate = it.toDoubleOrNull()
-                        interestRateError = rate == null || rate <= 0 || rate >= 100
+                        val input = it.trim()
+                        interestRate = input
+                        val rate = input.toDoubleOrNull()
+                        interestRateError = rate == null || rate < 0 || rate > 100
                     },
                     label = { Text("Interest Rate (%)") },
                     isError = interestRateError,
+                    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
+                    singleLine = true,
                     supportingText = {
                         if (interestRateError) {
                             Text(
-                                "Please enter a valid percentage (0-100)",
+                                "Please enter a valid percentage (0–100 inclusive)",
                                 color = Color(0xFFD32F2F)
                             )
                         }
                     },
                     modifier = Modifier.fillMaxWidth(),
                     shape = RoundedCornerShape(8.dp)
                 )
@@
-            Button(
+            Button(
                 onClick = {
-                    val rate = interestRate.toDoubleOrNull()
-                    if (rate != null && rate > 0 && rate < 100) {
+                    val rate = interestRate.trim().toDoubleOrNull()
+                    if (rate != null && rate >= 0 && rate <= 100) {
                         onConfirm(
                             RateChartItem(
                                 amountRange = amountRange,
                                 description = description,
                                 period = period,
                                 interestRate = "$interestRate%"
                             )
                         )
                     }
                 },
-                enabled = !interestRateError && interestRate.isNotEmpty(),
+                enabled = !interestRateError && interestRate.isNotEmpty(),
                 shape = RoundedCornerShape(8.dp),
                 colors = ButtonDefaults.buttonColors(
                     containerColor = Color(0xFF5C6BC0)
                 )
             ) {
                 Text("Save", fontWeight = FontWeight.Medium)
             }
🤖 Prompt for AI Agents
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt
lines 821-933: current validation treats bounds as exclusive and parses raw
input, plus it doesn’t force a numeric keyboard; update the interestRate
handling to trim and strip any '%' before parsing (use
interestRate.trim().replace("%","")), use KeyboardOptions(keyboardType =
KeyboardType.Decimal) on the OutlinedTextField to force numeric input, change
validation to allow inclusive bounds (error when rate == null || rate < 0 ||
rate > 100) and use the same trimmed/cleaned value when converting for the
confirm action so saved interestRate uses a normalized value with a trailing
"%".

@KadariPavani KadariPavani force-pushed the MIFOSAC-560-new-fixed-deposits-account-interest-charts-step branch from 20d4797 to efc0851 Compare October 27, 2025 17:37
@KadariPavani KadariPavani reopened this Oct 27, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt (2)

210-215: This issue was already flagged in a previous review. After removing an item, selectedItemIndex must be reset to avoid pointing at a stale or out-of-bounds index.


878-897: The validation bounds mismatch and missing numeric keyboard were already flagged in a previous review. The validation should be inclusive (0-100) to match the error message, and KeyboardOptions(keyboardType = KeyboardType.Decimal) should be added.

🧹 Nitpick comments (3)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt (3)

854-876: Consider adding validation for other fields.

The amountRange, description, and period fields accept any input, including empty strings. While interestRate has validation, saving items with empty or invalid values in other fields could degrade UX.

Consider adding:

  • Format validation for amountRange (e.g., "$200 - $250")
  • Non-empty validation for description and period
  • Enable/disable the Save button based on all field validation states

726-731: Add content descriptions for accessibility.

The Edit and Delete icons lack contentDescription values, which impacts screen reader accessibility. While the text labels are present, ensure each icon has a proper description for users relying on assistive technologies.

Apply this pattern:

 Icon(
     imageVector = Icons.Default.Edit,
-    contentDescription = "Edit",
+    contentDescription = null,  // decorative since text is present
     tint = Color(0xFF757575),
     modifier = Modifier.padding(end = 12.dp)
 )

Or provide meaningful descriptions if the icons can be interacted with independently of the text.

Also applies to: 762-767


158-161: Placeholder download functionality requires implementation.

The download callbacks are currently no-ops. Ensure these are implemented before release or add TODO comments to track the work.

Do you want me to help outline the download implementation or open an issue to track this task?

Also applies to: 181-183

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20d4797 and 36a20ad.

📒 Files selected for processing (1)
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt (1 hunks)

@KadariPavani
Copy link
Author

@KadariPavani can you upload the screenshots what you have updated

@revanthkumarJ ,

please review the code as i updated the intrest chart page with the requirements that got from the issue
Here are the output screenshots

WhatsApp Image 2025-10-27 at 23 30 02_de8b02f9
image
image

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt (4)

329-339: Replace hard-coded strings/colors with resources and theme tokens (i18n + dark mode).

Use stringResource(...) and MaterialTheme.colorScheme/typography. Sample edits below; apply the pattern broadly across this file.

-                TextButton(
-                    onClick = onViewClick,
-                    shape = RoundedCornerShape(4.dp),
-                ) {
-                    Text(
-                        text = "View",
-                        color = MaterialTheme.colorScheme.primary,
-                        fontSize = 14.sp,
-                        fontWeight = FontWeight.Medium,
-                    )
-                }
+                TextButton(onClick = onViewClick, shape = RoundedCornerShape(4.dp)) {
+                    Text(stringResource(Res.string.view))
+                }
@@
-        Surface(
+        Surface(
             modifier = Modifier
                 .fillMaxWidth(0.92f)
                 .padding(horizontal = 16.dp),
             shape = RoundedCornerShape(20.dp),
-            color = Color.White,
+            color = MaterialTheme.colorScheme.surface,
         ) {
@@
-                Text(
-                    text = "Rate Chart",
-                    style = MaterialTheme.typography.titleLarge,
-                    fontWeight = FontWeight.Bold,
-                    fontSize = 22.sp,
-                    color = Color(0xFF212121),
-                    modifier = Modifier.padding(bottom = 20.dp),
-                )
+                Text(
+                    text = stringResource(Res.string.rate_chart_title),
+                    style = MaterialTheme.typography.titleLarge,
+                    color = MaterialTheme.colorScheme.onSurface,
+                    modifier = Modifier.padding(bottom = 20.dp),
+                )
@@
-                    OutlinedButton(
+                    OutlinedButton(
                         onClick = onDismiss,
                         modifier = Modifier.weight(1f),
                         shape = RoundedCornerShape(8.dp),
-                        border = BorderStroke(1.dp, Color(0xFFBDBDBD)),
+                        border = BorderStroke(1.dp, MaterialTheme.colorScheme.outline),
                     ) {
-                        Text(
-                            "Back",
-                            color = Color(0xFF212121),
-                            fontSize = 15.sp,
-                            fontWeight = FontWeight.Medium,
-                        )
+                        Text(stringResource(Res.string.back))
                     }
@@
-                    Button(
+                    Button(
                         onClick = onDownload,
                         modifier = Modifier.weight(1f),
                         shape = RoundedCornerShape(8.dp),
-                        colors = ButtonDefaults.buttonColors(
-                            containerColor = Color(0xFF5C6BC0),
-                        ),
+                        colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary),
                     ) {
-                        Text(
-                            "Download",
-                            fontSize = 15.sp,
-                            fontWeight = FontWeight.Medium,
-                        )
+                        Text(stringResource(Res.string.download))
                     }

Verification helper to find literals:

#!/bin/bash
rg -nP '\bText\(\s*"|label\s*=\s*\{\s*Text\("|\bColor\(' -g '!**/build/**' feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt

As per coding guidelines.

Also applies to: 398-405, 446-459, 390-393, 291-296, 352-359, 361-369


205-210: Reset selection after delete to avoid stale index.

After removal, selectedItemIndex still points to the deleted row. Reset it.

             onConfirm = {
                 rateChartItems.removeAt(selectedItemIndex)
+                selectedItemIndex = -1
                 showDeleteConfirmation = false
                 showRateChartModal = true
             }

829-848: Make 0–100% validation inclusive and force numeric keyboard; sanitize input.

Spec says 0–100%; current check is exclusive and allows stray '%' input to fail silently.

                 OutlinedTextField(
-                    value = interestRate,
-                    onValueChange = {
-                        interestRate = it
-                        val rate = it.toDoubleOrNull()
-                        interestRateError = rate == null || rate <= 0 || rate >= 100
-                    },
+                    value = interestRate,
+                    onValueChange = {
+                        val input = it.trim().replace("%", "")
+                        interestRate = input
+                        val rate = input.toDoubleOrNull()
+                        interestRateError = rate == null || rate < 0 || rate > 100
+                    },
                     label = { Text("Interest Rate (%)") },
                     isError = interestRateError,
+                    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
+                    singleLine = true,
                     supportingText = {
                         if (interestRateError) {
                             Text(
-                                "Please enter a valid percentage (0-100)",
+                                "Please enter a valid percentage (0–100 inclusive)",
                                 color = Color(0xFFD32F2F),
                             )
                         }
                     },
                 )
@@
             Button(
                 onClick = {
-                    val rate = interestRate.toDoubleOrNull()
-                    if (rate != null && rate > 0 && rate < 100) {
+                    val rate = interestRate.trim().toDoubleOrNull()
+                    if (rate != null && rate >= 0 && rate <= 100) {
                         onConfirm(
                             RateChartItem(
                                 amountRange = amountRange,
                                 description = description,
                                 period = period,
-                                interestRate = "$interestRate%",
+                                interestRate = "$interestRate%",
                             ),
                         )
                     }
                 },

Add imports:

import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.KeyboardType

Also applies to: 852-865


57-73: Constrain API surface and prefer strong types for numeric data.

  • Make these models internal to avoid leaking unstable UI types.
  • Replace stringly-typed numeric fields with numeric types; format in the UI only.

Minimal visibility change (safe now):

-data class RateChartItem(
+internal data class RateChartItem(
@@
-)
+)
@@
-data class ChartInformation(
+internal data class ChartInformation(
@@
-)
+)

Recommended type refactor (apply when wiring ViewModel data):

internal data class RateChartItem(
    val amountRangeMin: Double,
    val amountRangeMax: Double,
    val description: String,
    val periodYears: Int,
    val interestRatePercent: Double
)

This removes parsing bugs and aligns with validation. Confirm if these models must be public; if not, keep them internal.

🧹 Nitpick comments (4)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt (4)

413-423: Use LazyColumn for potentially long lists.

Avoid Column with forEachIndexed for lists; use LazyColumn for performance and better memory use.

-                Column(
-                    modifier = Modifier.weight(1f, fill = false),
-                    verticalArrangement = Arrangement.spacedBy(12.dp),
-                ) {
-                    rateChartItems.forEachIndexed { index, item ->
-                        RateChartItemCard(
-                            item = item,
-                            onClick = { onItemClick(index) },
-                        )
-                    }
-                }
+                LazyColumn(
+                    modifier = Modifier.weight(1f),
+                    verticalArrangement = Arrangement.spacedBy(12.dp),
+                ) {
+                    itemsIndexed(rateChartItems) { index, item ->
+                        RateChartItemCard(item = item, onClick = { onItemClick(index) })
+                    }
+                }

Add imports:

import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed

275-279: Use vector icons (and hide decorative ones) instead of glyph text.

Replace "▲" and "›" text with Icons.* and set contentDescription = null so screen readers don’t announce them.

-                Text(
-                    text = "▲",
-                    fontSize = 10.sp,
-                    color = Color(0xFF4CAF50),
-                )
+                Icon(
+                    imageVector = Icons.Default.ArrowDropUp,
+                    contentDescription = null,
+                    tint = Color(0xFF4CAF50),
+                )
@@
-                Text(
-                    text = "›",
-                    fontSize = 24.sp,
-                    color = Color(0xFF9E9E9E),
-                    modifier = Modifier.padding(start = 2.dp),
-                )
+                Icon(
+                    imageVector = Icons.Default.KeyboardArrowRight,
+                    contentDescription = null,
+                    tint = Color(0xFF9E9E9E),
+                    modifier = Modifier.padding(start = 2.dp),
+                )

Also applies to: 532-537


75-103: Hoist UI state to ViewModel; avoid mutable state in the page for prod.

Sample data + in-page state is fine for a prototype, but wire this to FixedDepositAccountViewModel and a UiState to survive recomposition and configuration changes. Also derive “Next enabled” from state (non-empty validated chart).


219-223: Remove zero-width transparent borders.

BorderStroke(0.dp, Color.Transparent) is a no-op; drop it to simplify.

-        border = BorderStroke(0.dp, Color.Transparent),
+        // border removed

Also applies to: 662-667

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e46b8fa and ed283b6.

📒 Files selected for processing (1)
  • feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/InterestPage.kt (1 hunks)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants