Implement CategoryScreen, ImportBankStatementScreen, SettingsScreen, and ExportAction helper #7
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Overview
Implements four pieces:
ui/components/ExportAction.kt— shared export helper (date range dialog + SAFCreateDocumentlauncher)ui/screens/category/CategoryScreen.kt— category list with add/edit/deleteui/screens/import_bank_statement/ImportBankStatementScreen.kt— 1 Voyager Screen, 1 ScreenModel, 2 composablesui/screens/settings/SettingsScreen.kt— usesPreferenceScreenDepends on #2, #3, #4.
Prerequisites
Issues #1, #2, #3, #4 must all be merged first.
What to do
1.
ui/components/ExportAction.kt— shared export helperA composable that:
Booleanstate for the date range dialog.ActivityResultLauncher<String>forActivityResultContracts.CreateDocument("text/csv").IconButton(Export icon) that, on click, opens the date range dialog.DatePickerfields (start, end). On confirm, callslauncher.launch("ledgerr-export-YYYY-MM-DD.csv")(or similar default filename).Uri, calls the active ScreenModel's export method with that URI.onExportConfirmed: (DateRange, Uri) -> Unit(the screen model implements the actualExportExpensesToCsv.await(...)call).Signature:
Used by
HomeScreen(issue #5),ExpenseListScreen(issue #6), andSettingsScreen(this issue).2.
ui/screens/category/CategoryScreen.kt+CategoryScreenModel.ktgetCategories.subscribeAll().deleteCategory.await(id). The reassignment-to-Uncategorized logic happens insideDeleteCategory.isDefault = trueshow a lock icon and cannot be deleted.3.
ui/screens/import_bank_statement/ImportBankStatementScreen.kt+ImportBankStatementScreenModel.ktOne Voyager
Screen, oneScreenModel, two internal composables switched byStateFlow<ImportState>:ImportBankStatementPickerContent— rendered when state isBankPickerorProcessing:bankName).ActivityResultContracts.OpenDocument(arrayOf("application/pdf"))viarememberLauncherForActivityResult. On URI returned, sets state toProcessingand triggersscreenModel.processPdf(uri, importer).Processing, render aCircularProgressIndicatoroverlay on top.ImportBankStatementConfirmationContent— rendered when state isConfirmation:isSelected), amount, date, description, category dropdown (driven bygetCategories.subscribeAll()).isSelected == true.Expense(amount, categoryId ?: defaultCategoryId, date, note = description, ...)and callinsertExpenses.awaitAll(...).navigator.pop().ScreenModel injects:
getImporters: List<BankStatementImporter> = inject(),getCategories,insertExpenses,defaultCategory(resolved viagetCategories.awaitDefault()lazily).The 3 bank stubs from #4 will return
NotImplementedError; the ScreenModel should catch that and surface a snackbar / error message — not crash.4.
ui/screens/settings/SettingsScreen.kt+SettingsScreenModel.ktUses the
PreferenceScreencomposable fromui/components/preference/:SettingsScreenModelinjectsclearData: ClearAllData,seedDefaultCategories: SeedDefaultCategories:For Export, the simplest is to embed
ExportActiondirectly as a row inPreferenceScreen(or call itsonClickfrom theTextPreference). TheExportActioncomposable provides the dialog + SAF launcher; the ScreenModel callsexportExpensesToCsv.await(range, uri).Acceptance
ExportActioncomposable is reusable across screensCategoryScreenallows add/edit/delete; default categories cannot be deletedImportBankStatementScreenshows bank picker, processing overlay, and confirmation viewImportBankStatementScreenhandlesNotImplementedErrorgracefully (snackbar/error)SettingsScreenexposes theme, export, and clear-data preferencesClearAllData+ re-seed works end-to-end from the settings dialog./gradlew assembleDebugsucceedsImplementation rule
Per
AGENTS.md— do not start implementation without explicit user sign-off on this issue. When working, check for related issues in the remote repo first.