Commit Graph

37 Commits

Author SHA1 Message Date
Achmad Setyabudi Susilo 5953111897 fix(#25): render expense tab counts via TabText
Replace the plain Text inside PrimaryTabRow's Tab with the shared
TabText composable. Pass the current size of the expenses and
recurring state flows as badgeCount so each tab shows a small pill
with the (post-filter) list size. Counts update reactively as
items are added, deleted, or filtered by search/date.
2026-06-28 21:22:09 +07:00
Achmad Setyabudi Susilo b698f5084f fix(#28): add scrim when ExpandedFab is open
Add ExpandedFabScrim composable that renders a Material 3 scrim
overlay fading in/out in sync with the mini-FABs. Tapping the scrim
dismisses the FAB.

Move the ExpandedFab out of Scaffold's floatingActionButton slot and
into the body inside a Box, so the scrim can match the body size via
matchParentSize() and stack above the list but below the FAB. Add a
BackHandler that dismisses the FAB on system back while it is open.
2026-06-28 21:21:36 +07:00
admin ba99eac4be Merge pull request 'fix(#21): move DB I/O to Dispatchers.IO in AddEdit screen models' (#22) from feat/21-move-db-io-to-dispatchers-io into main
Reviewed-on: #22
2026-06-28 13:30:20 +00:00
admin 006d3693ab Merge pull request 'Implement HomeScreen with Vico dashboard (#5)' (#20) from feat/5-implement-homescreen-with-vico-dashboard into main
Reviewed-on: #20
2026-06-28 13:29:41 +00:00
Achmad Setyabudi Susilo 0bf47d5c94 fix(#21): move DB I/O to Dispatchers.IO in AddEdit screen models
Wrap getExpenses.awaitOne, upsertExpense.await, getRecurringExpenses.awaitOne
and upsertRecurringExpense.await in withContext(Dispatchers.IO) { ... } so
the suspending Room calls run off the main thread. State-flow updates stay
inside screenModelScope.launch, which is Main-bound, and execute after the
withContext block returns.
2026-06-28 20:28:46 +07:00
Achmad Setyabudi Susilo 3ddfaa0a22 fix(#5): address PR review — share ExpandedFab, inject GetExpenseSummary, column-chart spec
- Add ui/components/ExpandedFab.kt with ExpandedFab + MiniFab helpers; HomeScreen and ExpenseListScreen both consume it, the tab-collapsing LaunchedEffect in ExpenseListScreen is hoisted to its Content()
- Inject GetExpenseSummary in HomeScreenModel; drive summary via dateRange.flatMapLatest { getExpenseSummary.await(it) } (fixes the period-filter total-card flicker) and drop the inline combine(expenses, dateRange) recomputation
- Hoist isFabExpanded out of HomeScreenModel into HomeScreen.Content() so the FAB state is local to the composable
- Convert HomeScreenModel.exportToCsv from a callback to a suspend fun returning Result<Unit>; the screen does the snackbar dispatch on the coroutineScope
- Consolidate DateRangeOption label mapping to a single DateRangeOption.labelRes() / .labelText() pair (one source of truth)
- Rename FAB string keys to shared fab_manual / fab_import and drop the home_fab_* duplicates
- Update docs/04-implementation-plan.md and .opencode/agent/implementor.md Charts sections to reflect the Vico 2.0.0 column-chart-with-legend substitution (Vico 2.0.0 has no pie layer)
2026-06-28 20:25:53 +07:00
Achmad Setyabudi Susilo a0ccf22e67 feat(#5): implement HomeScreen with Vico dashboard
- Add HomeScreenModel with expenses/summary/recurring-banner/fab state flows and a getExpenses + processDueRecurring + exportExpensesToCsv + expensePreference constructor
- Replace the HomeScreen stub with a Material 3 dashboard: AppBar (Export + Settings), total card, period filter, Vico ColumnCartesianLayer chart with per-category legend, manage-categories/see-all actions, recent expenses, and an expanded FAB exposing Manual + Import sub-actions
- Add home strings and a home_recurring_banner plurals resource
2026-06-28 20:11:01 +07:00
admin 8ce0dcc678 Merge pull request 'Implement ExpenseListScreen, AddEditExpenseScreen, AddEditRecurringScreen (#6)' (#18) from feat/6-implement-expense-list-add-edit-screens into main
Reviewed-on: #18
2026-06-28 12:57:17 +00:00
admin a3c0976edd Merge pull request 'chore(#9): enable Room exportSchema and configure schemaLocation' (#19) from chore/9-enable-room-exportschema into main
Reviewed-on: #19
2026-06-28 12:55:34 +00:00
Achmad Setyabudi Susilo ed8b3577dc chore(#9): enable Room exportSchema and configure schemaLocation 2026-06-28 19:51:33 +07:00
Achmad Setyabudi Susilo e8c7a14c75 fix(#6): address PR review
- matchesQuery: use %.2f format to match the display (was Double.toString, which can use scientific notation and disagree with the row's %.2f)
- Remove unused Switch import in AddEditRecurringScreen (the active toggle uses ToggleItem)
- Extract CategoryDropdownField to ui/components/CategoryDropdownField.kt so both add/edit screens share one implementation; takes label as a parameter
- Remove unused recurring_list_active string
2026-06-28 19:49:02 +07:00
Achmad Setyabudi Susilo b0a62bedf0 feat(#6): implement ExpenseListScreen, AddEditExpenseScreen, AddEditRecurringScreen 2026-06-28 19:36:20 +07:00
admin 6f7d24a303 Merge pull request 'Implement CategoryScreen, ImportBankStatementScreen, SettingsScreen, and ExportAction helper (#7)' (#17) from feat/7-implement-screens-export into main
Reviewed-on: #17
2026-06-28 12:06:47 +00:00
Achmad Setyabudi Susilo 22863bfcd6 fix(#7): emit snackbar as resource id, resolve with stringResource in UI
Drop the Context injection from the ScreenModel. Emit a sealed
ImportSnackbarMessage carrying either a @StringRes id or a dynamic
text (for the error.message case where the bank importer surfaces a
useful 'BRI import not yet implemented' string). The screen resolves
resource ids via stringResource() in Composable scope and shows the
snackbar; the original error.message info is preserved via the
Dynamic variant instead of being dropped.
2026-06-28 18:46:17 +07:00
Achmad Setyabudi Susilo 5893ffc955 fix(#7): localize import snackbar strings and drop class.simpleName fallback
The three snackbar strings in ImportBankStatementScreenModel were
hardcoded English while the rest of the app uses stringResource. Move
them to res/values/strings.xml. Also drop the
error::class.simpleName fallback in the failure branch — it surfaced
Kotlin class names like NotImplementedError to end users. Now falls
back to the localized 'Import failed' string.
2026-06-28 18:32:58 +07:00
Achmad Setyabudi Susilo 1a71c4c9e6 chore(agents): require reviewer to use COMMENT state (same-account Gitea MCP)
Gitea blocks self-approval and self-requested-changes, so the reviewer
agent (which always reviews its own work on this same-account setup)
must always submit with state: COMMENT. Verdicts go in the summary body;
blocking concerns are marked inline with **Blocking:** / **Nit:** /
**Suggestion:** prefixes so the implementor can triage them.
2026-06-28 18:32:47 +07:00
Achmad Setyabudi Susilo ce01c175df fix(#7): use UTC for DateField initial seed to match pick conversion
DateField seeded the picker with ZoneId.systemDefault() but converted
the selectedDateMillis back with ZoneId.of("UTC"). For any non-UTC
user, the field and the picker displayed different days, and
confirming without re-picking silently shifted the export range by a
day. Use UTC on both sides (the DatePickerState contract is that
selectedDateMillis is UTC midnight of the picked day).
2026-06-28 18:27:49 +07:00
Achmad Setyabudi Susilo 7782df8b36 fix(#7): move DB/IO off main thread in ScreenModels
screenModelScope is backed by PlatformMainDispatcher (Main.immediate),
so direct interactor calls run DB queries and file I/O on the UI
thread. Switch reactive flows with .flowOn(Dispatchers.IO) and wrap
suspend calls in withContext(Dispatchers.IO).
2026-06-28 18:10:35 +07:00
Achmad Setyabudi Susilo f6860544e4 feat(#7): implement CategoryScreen, ImportBankStatementScreen, SettingsScreen, and ExportAction helper 2026-06-28 17:55:22 +07:00
Achmad Setyabudi Susilo 567f6a7cee chore(agents): document local.properties resolution for worktrees 2026-06-28 17:53:17 +07:00
admin 36deb46a28 Merge pull request 'Implement bankstatement, export, and data interactors (#4)' (#16) from feat/4-implement-bankstatement-export-data into main
Reviewed-on: #16
2026-06-28 10:42:01 +00:00
Achmad Setyabudi Susilo 94d40d4216 feat(#4): implement bankstatement, export, and data interactors 2026-06-28 17:37:04 +07:00
Achmad Setyabudi Susilo 105c858d57 chore(agents): split AGENTS.md into shared + per-role agents
Move architecture, package structure, dependencies, and implementation
rules into .opencode/agent/implementor.md (default primary agent). Add
.opencode/agent/reviewer.md (read-only primary agent) with PR review
workflow that leaves inline review comments only -- no issue creation,
no edits, no commits. AGENTS.md is now the shared context both agents
load: workflow, git/PR conventions, and docs index. Set
default_agent: implementor in opencode.json.
2026-06-28 17:06:06 +07:00
admin 1bb6747610 Merge pull request 'Implement expense interactors (#2)' (#11) from feat/2-implement-expense-interactors into main
Reviewed-on: #11
2026-06-28 09:58:45 +00:00
Achmad Setyabudi Susilo ca2992fceb Merge branch 'main' of https://git.achmad.dev/admin/ledgerr into feat/2-implement-expense-interactors
# Conflicts:
#	app/src/main/java/dev/achmad/ledgerr/di/DomainModule.kt
2026-06-28 16:57:58 +07:00
admin ec21462d03 Merge pull request 'Implement recurring interactors (#3)' (#10) from feat/3-implement-recurring-interactors into main
Reviewed-on: #10
2026-06-28 09:56:38 +00:00
Achmad Setyabudi Susilo 63bfe2a6b5 fix(#2): address PR review — use row mapper, require id==0, drop ignoreCase on amount, doc drift, orphan-category note 2026-06-28 16:52:42 +07:00
Achmad Setyabudi Susilo 7bb65025a2 fix(#3): make ProcessDueRecurringExpenses atomic via withTransaction
Address review: insert + advance pair must run in one DB transaction to
prevent duplicate expenses if the process is killed mid-loop.
2026-06-28 16:52:07 +07:00
Achmad Setyabudi Susilo 6a11284212 feat(#3): implement recurring interactors 2026-06-28 16:33:41 +07:00
Achmad Setyabudi Susilo 46f882b3c3 feat(#2): implement expense interactors 2026-06-28 16:33:39 +07:00
Achmad Setyabudi Susilo 179f5fe2f8 docs + AGENTS: worktree workflow, predictable branch naming, PR body template, review iteration, scope discipline 2026-06-28 16:27:37 +07:00
admin c22c33ab7e Merge pull request 'Implement category feature and wire DI foundation (#1)' (#8) from feat/1-category-foundation into main
Reviewed-on: #8
2026-06-28 09:17:22 +00:00
Achmad Setyabudi Susilo 547343992a fix(#1): address PR review — split upsert into insert/update, add @Transaction, runBlocking seed, trailing newline
Per review on PR #8 (#8):

- Split @Upsert into @Insert(OnConflictStrategy.REPLACE) + @Update in all 3 DAOs.
  @Upsert returns -1 on the update path, so callers wanting the row ID would
  get a junk value. Interactors now call insert vs update based on id == 0.
  UpsertCategory returns category.id explicitly for the id != 0 branch.
- Add @Transaction to the 3 @Relation queries (ExpenseDao.subscribeAll,
  ExpenseDao.subscribeByDateRange, RecurringExpenseDao.subscribeAll). This
  silences the KSP warnings the PR body mentioned and makes the intent
  explicit.
- Switch MainApplication seeding from a fire-and-forget CoroutineScope to
  runBlocking(Dispatchers.IO). A fast first-tap on HomeScreen could otherwise
  call GetCategories.awaitDefault() before seeding completed and crash.
- Add documenting comment on CategoryDao.getDefault() noting that the
  'only one isDefault = 1' invariant is maintained at the interactor layer
  (partial unique index would be a v2 migration).
- Add trailing newline to app/build.gradle.kts.
- File follow-up issue #9 for flipping exportSchema to true before any v2
  migration lands.
2026-06-28 16:09:15 +07:00
Achmad Setyabudi Susilo 8e7a6cfe3f feat(#1): implement category feature and wire DI foundation
- Add Room 2.7.1, PDFBox-Android 2.0.27.0, Vico 2.0.0 dependencies
- Bump compileSdk 36 -> 37 (transitive deps require it)
- Add LocalDateConverter + 3 entities + 3 DAOs with @Relation rows + mappers + AppDatabase v1
- Add all domain models (expense, category, recurring, bankstatement, preference)
- Implement 4 category interactors: GetCategories, UpsertCategory, DeleteCategory, SeedDefaultCategories
- Add minimal ReassignExpenseCategory (full expense interactors come in #2)
- Wire CoreModule (SharedPreferences + AndroidPreferenceStore), DataModule (Room + 3 DAOs), PreferenceModule, DomainModule (category factories)
- Create MainApplication with Koin start and SeedDefaultCategories trigger
- Register MainApplication in AndroidManifest
- Add missing string resources for pre-existing copied UI components (confirm, cancel, ok, general_selected, general_not_selected, disabled)
2026-06-28 15:53:58 +07:00
admin 51c54749cb docs + AGENTS: no-implementation rule, issue-driven workflow, Vico, Uncategorized, ClearAllData, AddEditRecurringScreen
- AGENTS.md: add No-implementation rule and Issue-driven workflow section
- docs/01: rename default fallback category from 'Other' to 'Uncategorized'
- docs/02, 03: update 'Other' references to 'Uncategorized' in delete/seed/default flows
- docs/04: replace Canvas charts with Vico 2.x dep, add data feature folder, add AddEditRecurringScreen, document tab-aware FAB and shared ExportAction helper
2026-06-28 15:47:28 +07:00
Achmad Setyabudi Susilo 3e30423083 docs: add architecture docs, AGENTS.md, CLAUDE.md, and copy UI components
- Add docs/01-04 covering data model, interfaces, function TODOs, and implementation plan
- Add AGENTS.md with project conventions, architecture rules, feature workflow, and git policy
- Add CLAUDE.md pointing to AGENTS.md
- Copy UI components and utils from info-krl-android (preference widgets, scrollbars, etc.)
- Delete obsolete UiModule.kt
2026-06-28 15:08:37 +07:00
Achmad Setyabudi Susilo dfca375a9b chore: setup project 2026-06-28 13:21:57 +07:00