New Gradle module :shared (pure Kotlin/JVM) containing @Serializable
DTO classes for use by both the Android app and future Ktor server.
shared/src/main/kotlin/de/krisenvorrat/shared/model/:
- InventoryDto: root DTO replacing ExportData (version, categories,
locations, items, settings)
- CategoryDto, LocationDto, ItemDto, SettingDto: extracted from
the former *Export data classes in :app
Migration in :app:
- ExportData.kt deleted (classes moved to :shared)
- ImportExportRepositoryImpl now imports from de.krisenvorrat.shared.model
- app/build.gradle.kts adds implementation(project(:shared))
Build config:
- libs.versions.toml: added kotlin-jvm plugin entry
- build.gradle.kts (root): registered kotlin-jvm plugin
- settings.gradle.kts: include(:shared)
JSON wire format is unchanged; all 165 existing tests pass.
Closes#39
SettingsScreen: Added import button that opens the system file picker
(ActivityResultContracts.OpenDocument) filtered to application/json.
After file selection, a confirmation dialog warns that existing data
will be overwritten. Import result is shown in a success/error dialog.
SettingsViewModel: Added onImportFileSelected(uri), onImportConfirmed(),
onImportDismissed(), onImportResultDismissed() methods. The import reads
the file via contentResolver.openInputStream() and delegates to the
existing ImportExportRepository.importFromJson(). Settings are reloaded
after successful import.
SettingsUiState: Extended with isImporting, importResult (sealed
interface ImportResult with Success/Error), and pendingImportUri for
the confirmation dialog flow.
SettingsViewModelTest: Added 6 unit tests covering import success,
invalid JSON error, empty file, null input stream, dialog state
management, and result dismissal.
Closes#38
Implement export functionality in the Settings screen allowing users to
share their inventory data as JSON (via FileProvider + ACTION_SEND with
EXTRA_STREAM) or Markdown (via ACTION_SEND with EXTRA_TEXT).
Key changes:
- ShareContent sealed interface for export events (Json with URI,
Markdown with text)
- SettingsViewModel: exportJson() writes to cache file and creates
FileProvider URI; exportMarkdown() provides text directly
- SettingsUiState: isExporting, shareContent, exportError fields
- SettingsScreen: LaunchedEffect consumes share events and opens
Android Share Sheet via Intent.createChooser
- FileProvider registered in AndroidManifest with cache-path config
- MockK added as test dependency for FileProvider static mocking
- 8 new unit tests covering export success, failure, and state cleanup
Closes#37
Implement Markdown export for the entire inventory (Issue #36).
The method renders categories as headings with items in a table
(Name, Menge, Einheit, MHD, Lagerort). Empty categories are skipped.
Dates are formatted as dd.MM.yyyy (German), quantities use German
decimal format (comma). Settings section shows household_size and
kcal_per_day if present.
Includes 6 unit tests covering: full export, empty categories,
missing expiry date, settings section, fractional quantities, and
irrelevant settings omission.
Closes#36
Implement the full Settings tab with household size and daily kcal/person
input fields, persisted via Room through the existing SettingsRepository.
The DashboardViewModel now reads settings reactively and passes them to
CalculateSupplyRangeUseCase instead of using hardcoded defaults.
Changes:
- Add observeValue(key) Flow method to SettingsDao and SettingsRepository
- Create SettingsViewModel with load/save logic and input validation
- Create SettingsUiState data class
- Replace SettingsScreen placeholder with full Compose UI
(household size, kcal/day fields, save button, export/import placeholders)
- Integrate settings into DashboardViewModel via combine() with 4 flows
- Add SettingsViewModelTest (6 tests covering defaults, persistence, validation)
- Update DashboardViewModelTest and test fakes for new constructor parameter
Closes#35
ui/warnings/WarningsScreen.kt: full implementation replacing placeholder.
Shows expiry warnings (colored by urgency: URGENT=error, WARNING=orange)
and min-stock warnings as individual cards in a LazyColumn. Displays
empty state when no warnings exist.
ui/warnings/WarningsViewModel.kt: HiltViewModel observing ItemRepository
flow, delegates to GetExpiryWarningsUseCase and GetMinStockWarningsUseCase.
Exposes WarningsUiState via StateFlow.
ui/warnings/WarningsUiState.kt: data class with expiryWarnings,
minStockWarnings, isLoading, and derived properties (totalWarningCount,
hasWarnings).
ui/dashboard/DashboardScreen.kt: replaced ExpiryWarningsCard and
MinStockWarningsCard with compact WarningsSummaryCard showing only
warning counts. Removed unused domain model imports.
tests: 7 WarningsViewModel unit tests covering empty state, expiry
warnings, min-stock warnings, combined counts, reactive updates.
Closes#34
Color.kt: New file with M3 color tokens generated from olive green
seed color #4A6741. Defines primary, secondary, tertiary, error,
surface, and container colors for the dark color scheme.
Theme.kt: Updated DarkColorScheme with all custom color tokens,
changed default to darkTheme=true, status bar now uses surface
color instead of primary for better edge-to-edge appearance.
themes.xml: Changed splash theme parent from Material.Light to
Material dark, added dark background/status/navigation bar colors
matching the Compose surface color (#1A1C18).
Closes#32
Implement the Dashboard screen (Issue #30) as the new start destination:
- DashboardUiState: data class with sections for category summaries,
total value, supply range, expiry warnings, and min stock warnings
- DashboardViewModel: combines Item/Category flows with all five
Use Cases from #29 (CalculateCategorySummary, CalculateTotalValue,
CalculateSupplyRange, GetExpiryWarnings, GetMinStockWarnings)
- DashboardScreen: Material 3 layout with color-coded cards for
summary overview, supply range (days), expiry warnings (red/orange),
and min stock warnings (red), plus per-category cards
- Navigation: Dashboard added as startDestination, ItemListScreen
gets a Dashboard menu entry for back-navigation
- 9 unit tests covering empty state, category summaries, total value,
supply range, expiry/min-stock warnings, and reactive updates
Closes#30
domain/model/:
- CategorySummary: item count + total value per category
- ExpiryWarning + ExpiryUrgency: expiry date warnings (urgent ≤6mo, warning ≤12mo)
- MinStockWarning: items below minimum stock with deficit
domain/usecase/:
- CalculateTotalValueUseCase: sum of quantity × unitPrice
- CalculateCategorySummaryUseCase: per-category item count and value
- CalculateSupplyRangeUseCase: kcal-based supply range in days
(weight units g/kg/mg only, defaults 2 persons × 2000 kcal/day)
- GetExpiryWarningsUseCase: items expiring within 6/12 months
- GetMinStockWarningsUseCase: items where quantity < minStock
All use cases are pure functions with @Inject constructor() for Hilt.
39 unit tests covering all calculations including edge cases.
Closes#29
ItemFormViewModel:
- Create-Modus (new article) and Edit-Modus (load existing by ID via
SavedStateHandle navigation argument)
- Form state with all ItemEntity fields as MutableStateFlow
- Validation: name required, quantity > 0, category and location required
- Save function (insert for create, update for edit)
- Loads categories and locations for dropdown selection
ItemFormScreen:
- OutlinedTextField for name, quantity, unit, price, kcal/100g,
min stock, notes
- ExposedDropdownMenuBox for category and location selection
- Material 3 DatePickerDialog for expiry date (MHD)
- Inline validation error display per field
- Save button in TopAppBar, back navigation on successful save
- UUID generation for new articles
Tests:
- 18 unit tests covering create mode, edit mode, field updates,
validation (all required fields), and save behavior (insert vs update)
Closes#27
ui/item/ItemUiModel.kt:
- UI data class combining entity data with resolved category/location names
- Computed properties isExpired and isExpiringSoon for MHD color coding
ui/item/ItemListViewModel.kt:
- Combines ItemRepository, CategoryRepository, LocationRepository via Flow.combine
- Groups items by category name (sorted alphabetically)
- Delete flow with confirmation dialog state management
ui/item/ItemListScreen.kt:
- LazyColumn with category headers and Material 3 Cards per item
- Shows name, quantity+unit, location, and color-coded expiry date
- Delete via IconButton with AlertDialog confirmation
- Empty state when no items exist
- FAB with onAddItem navigation callback
ui/item/ItemListViewModelTest.kt:
- 9 unit tests covering init, grouping, name resolution,
delete dialog flow, and alphabetical sorting
Closes#26
Closes#25
ui/category/:
- CategoryListViewModel: StateFlow-based ViewModel with add/delete
dialog state management, backed by CategoryRepository
- CategoryListScreen: Material 3 Scaffold with LazyColumn, FAB for
adding, delete confirmation dialog with CASCADE warning
ui/location/:
- LocationListViewModel: same pattern for LocationRepository
- LocationListScreen: same UI pattern for location management
Tests:
- CategoryListViewModelTest: 11 tests covering init, add, delete,
dialog state, blank name rejection
- LocationListViewModelTest: 11 tests (same coverage)
Dependencies:
- Added lifecycle-runtime-compose for collectAsStateWithLifecycle
- Added kotlinx-coroutines-test for ViewModel unit tests
LocalDateConverterTest: added negative test for invalid string input
(DateTimeParseException).
CategoryDaoTest, LocationDaoTest: added getAll tests with multiple
entities to verify complete retrieval.
ItemDaoTest: fixed getExpiringSoon test (was calling non-existent
getExpiringSoon(Int) instead of getExpiringSoonByCutoff(LocalDate));
added getAll, getById positive, and getById negative tests.
JsonRoundtripTest (new): verifies lossless export-import roundtrip
with multiple items covering all fields, nullable fields (null
kcalPer100g, null expiryDate), and empty database edge case.
TestFakes (new): extracted shared Fake DAO implementations from
ImportExportRepositoryImplTest to avoid private class redeclaration
errors across test files in the same package.
Closes#22
domain/repository/ImportExportRepository.kt: new interface with
exportToJson() and importFromJson(json) suspend functions.
data/export/ExportData.kt: serializable data class bundling all
entity lists for JSON serialization via kotlinx.serialization.
data/export/ImportExportRepositoryImpl.kt: implementation using
kotlinx.serialization + Dispatchers.IO; exportToJson fetches all
DAOs and serializes, importFromJson deserializes and upserts back.
data/db/dao/{Category,Item,Location,Settings}Dao.kt: added @Upsert
upsertAll() suspend function to each DAO to support bulk import.
di/RepositoryModule.kt: bound ImportExportRepositoryImpl to
ImportExportRepository via Hilt @Binds.
test/.../FakeXxxDao.kt: upsertAll() implemented in all four fake
DAOs for unit test coverage.
- 4 Repository-Interfaces in domain/repository/ (Category, Location, Item, Settings)
- 4 Implementierungsklassen in data/repository/ mit Hilt @Inject
- RepositoryModule mit @Binds-Bindings fuer alle Repositories
- Datumslogik (getExpiringSoon) aus ItemDao in ItemRepositoryImpl verschoben
- 20 Unit-Tests mit Fake-DAOs (4 pro Repository)
- DatabaseModule: @Module + @InstallIn(SingletonComponent) with @Singleton-scoped
Room.databaseBuilder provider and four @Provides methods for ItemDao,
CategoryDao, LocationDao and SettingsDao
- DatabaseModuleTest: smoke-test verifies all four DAO providers return
non-null objects using an in-memory Room database
- KrisenvorratDatabase mit allen 4 Entities und LocalDateConverter
- CategoryDao, LocationDao, ItemDao, SettingsDao mit CRUD und Flow-Queries
- ItemDao.getExpiringSoon(daysUntil) als Default-Interface-Methode
- SettingsDao mit @Upsert (Room 2.6.1)
- Instrumentierungstests für alle 4 DAOs (in-memory DB)
- androidx.room:room-testing zu Dependencies ergänzt
- Add CategoryEntity, LocationEntity, SettingsEntity, ItemEntity
- ItemEntity: FK to Category+Location with CASCADE, indices on FK columns
- LocalDateConverter: LocalDate? <-> String? (ISO-8601) via @TypeConverter
- Add LocalDateConverterTest: 4 unit tests (null/non-null round-trip)
app/build.gradle.kts:
- Enabled buildConfig in buildFeatures to expose VERSION_NAME
app/src/main/java/de/krisenvorrat/app/MainActivity.kt:
- Replaced plain Text greeting with centered Column layout
- Shows app title 'Krisenvorrat' (headlineLarge) and version
'v1.0' via BuildConfig.VERSION_NAME (bodyMedium, onSurfaceVariant)
Verified: built, deployed to emulator, and confirmed via screenshot.