The SettingsViewModel manages the settings screen which includes account management and CRUD operations for all global catalog data. It follows the MVVM+MVI pattern with a single UI state and event-driven updates.
data class SettingsUiState( // Account tab val username: String = "", val roles: List<String> = emptyList(), val showLogoutConfirmation: Boolean = false, val isLoggingOut: Boolean = false, val isLogoutSuccessful: Boolean = false, // Tab selection val selectedTab: SettingsTab = SettingsTab.ACCOUNT, // Catalog loading val isCatalogsLoading: Boolean = true, val catalogsError: String? = null, // Device Categories, Types, Units val deviceCategories: List<DeviceCatalogCategory> = emptyList(), val deviceTypes: List<DeviceCatalogType> = emptyList(), val deviceUnits: List<DeviceCatalogUnit> = emptyList(), // Alert Types and Severities val alertTypes: List<AlertType> = emptyList(), val alertSeverities: List<AlertSeverityCatalog> = emptyList(), // Periods and Actuator States val periods: List<Period> = emptyList(), val actuatorStates: List<ActuatorState> = emptyList(), // Form dialogs and submission states for each catalog type // ... extensive state fields for CRUD operations)
The ViewModel handles events through a sealed interface with categories for each settings tab:
sealed interface SettingsEvent { // Account events data object OnLogoutClicked : SettingsEvent data object OnConfirmLogout : SettingsEvent data object OnCancelLogout : SettingsEvent // Tab selection data class OnTabSelected(val tab: SettingsTab) : SettingsEvent // Device Categories CRUD data object OnAddDeviceCategoryClicked : SettingsEvent data class OnEditDeviceCategoryClicked(val category: DeviceCatalogCategory) : SettingsEvent data class OnDeleteDeviceCategoryClicked(val category: DeviceCatalogCategory) : SettingsEvent data class OnSubmitDeviceCategory(val name: String) : SettingsEvent // Device Types CRUD data class OnSubmitDeviceType( val name: String, val description: String?, val categoryId: Short, val defaultUnitId: Short?, val dataType: String?, val minExpectedValue: Double?, val maxExpectedValue: Double?, val controlType: String?, val isActive: Boolean ) : SettingsEvent data class OnActivateDeviceType(val deviceType: DeviceCatalogType) : SettingsEvent data class OnDeactivateDeviceType(val deviceType: DeviceCatalogType) : SettingsEvent // ... similar events for other catalog types // Refresh data object OnRefreshCatalogs : SettingsEvent}
private fun loadAllCatalogs() { viewModelScope.launch { // Load all 7 catalog types in parallel val categoriesResult = catalogRepository.getDeviceCategories() val typesResult = catalogRepository.getDeviceTypes() val unitsResult = catalogRepository.getDeviceUnits() val alertTypesResult = catalogRepository.getAlertTypes() val severitiesResult = catalogRepository.getAlertSeverities() val periodsResult = catalogRepository.getPeriods() val actuatorStatesResult = catalogRepository.getActuatorStates() // Update state with sorted results _uiState.update { state -> state.copy( isCatalogsLoading = false, deviceCategories = categoriesResult.getOrDefault(emptyList()).sortedBy { it.id }, // ... other catalogs ) } }}
Each catalog type uses a sealed interface to track form mode:
sealed interface DeviceCategoryFormMode { data object Create : DeviceCategoryFormMode data class Edit(val category: DeviceCatalogCategory) : DeviceCategoryFormMode}sealed interface DeviceTypeFormMode { data object Create : DeviceTypeFormMode data class Edit(val deviceType: DeviceCatalogType) : DeviceTypeFormMode}// ... similar for other catalog types
This pattern allows the same dialog to be used for both create and edit operations.