feat: increment catalog version from 4 to 5 with comprehensive grocery domain restructuring and expanded item coverage
- Bump catalog version to 5 - Reorganize grocery domain structure with refined category hierarchy - Expand item definitions with enhanced metadata, aliases, and tagging - Add comprehensive bilingual support across all domains and categories - Include detailed emoji, color, and sort order specifications for improved UI presentation - Extend coverage across fruits, vegetables, grains, condiments, beverages, dairy, meat, produce, and specialty items
This commit is contained in:
parent
d569c344b0
commit
49eafca209
File diff suppressed because it is too large
Load Diff
@ -990,6 +990,7 @@ private fun SuggestionRow(
|
||||
is ListDetailViewModel.Suggestion.Active -> "Sur la liste" to MaterialTheme.colorScheme.tertiary
|
||||
is ListDetailViewModel.Suggestion.Recent -> "Recently Used" to LocalStatusColors.current.safe
|
||||
is ListDetailViewModel.Suggestion.Catalog -> suggestion.item.category to MaterialTheme.colorScheme.onSurfaceVariant
|
||||
is ListDetailViewModel.Suggestion.RoomCatalog -> (suggestion.categoryName ?: "Catalogue") to MaterialTheme.colorScheme.onSurfaceVariant
|
||||
is ListDetailViewModel.Suggestion.Create -> "Créer" to MaterialTheme.colorScheme.primary
|
||||
}
|
||||
Row(
|
||||
|
||||
@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
@ -87,6 +88,10 @@ class ListDetailViewModel @Inject constructor(
|
||||
override val label: String = item.name
|
||||
override val emoji: String = item.emoji
|
||||
}
|
||||
data class RoomCatalog(val item: CatalogItemEntity, val categoryName: String?) : Suggestion() {
|
||||
override val label: String = item.name
|
||||
override val emoji: String = item.emoji
|
||||
}
|
||||
data class Recent(val item: ShoppingListItemUi) : Suggestion() {
|
||||
override val label: String = item.productName
|
||||
override val emoji: String = item.emoji
|
||||
@ -152,31 +157,51 @@ class ListDetailViewModel @Inject constructor(
|
||||
val suggestions: StateFlow<List<Suggestion>> = combine(
|
||||
_searchQuery,
|
||||
state
|
||||
) { rawQuery, uiState ->
|
||||
) { rawQuery, uiState -> rawQuery to uiState }
|
||||
.flatMapLatest { (rawQuery, uiState) ->
|
||||
val query = rawQuery.trim()
|
||||
if (query.isEmpty()) return@combine emptyList()
|
||||
if (query.isEmpty() || uiState !is UiState.Ready) {
|
||||
return@flatMapLatest flowOf(emptyList())
|
||||
}
|
||||
|
||||
val ready = uiState as? UiState.Ready ?: return@combine emptyList()
|
||||
val results = mutableListOf<Suggestion>()
|
||||
val ready = uiState
|
||||
val staticResults = mutableListOf<Suggestion>()
|
||||
|
||||
// 1) Articles déjà sur la liste active (priorité haute pour rappel)
|
||||
ready.activeItems
|
||||
.filter { it.productName.contains(query, ignoreCase = true) }
|
||||
.take(2)
|
||||
.forEach { results.add(Suggestion.Active(it)) }
|
||||
.forEach { staticResults.add(Suggestion.Active(it)) }
|
||||
|
||||
// 2) Recently used → restauration rapide
|
||||
ready.recentlyUsed
|
||||
.filter { it.productName.contains(query, ignoreCase = true) }
|
||||
.take(3)
|
||||
.forEach { results.add(Suggestion.Recent(it)) }
|
||||
.forEach { staticResults.add(Suggestion.Recent(it)) }
|
||||
|
||||
// 3) Catalogue
|
||||
catalog.search(query, limit = 6)
|
||||
.filter { item ->
|
||||
results.none { it.label.equals(item.name, ignoreCase = true) }
|
||||
// 3) Catalogue statique (fallback si absent de la DB)
|
||||
val staticCatalogItems = catalog.search(query, limit = 20)
|
||||
.filter { item -> staticResults.none { it.label.equals(item.name, ignoreCase = true) } }
|
||||
|
||||
val categoryMap = catalogDomains.value.flatMap { it.categoriesWithItems }
|
||||
.associate { it.category.categoryId to it.category.name }
|
||||
|
||||
// 3bis) Catalogue Room (tous domaines/catégories + items futurs)
|
||||
catalogRepository.search(query, limit = 20).map { dbItems ->
|
||||
val results = staticResults.toMutableList()
|
||||
|
||||
dbItems.forEach { item ->
|
||||
if (results.none { it.label.equals(item.name, ignoreCase = true) }) {
|
||||
val catName = item.primaryCategoryId?.let { categoryMap[it] } ?: "Catalogue"
|
||||
results.add(Suggestion.RoomCatalog(item, catName))
|
||||
}
|
||||
}
|
||||
|
||||
staticCatalogItems.forEach { item ->
|
||||
if (results.none { it.label.equals(item.name, ignoreCase = true) }) {
|
||||
results.add(Suggestion.Catalog(item))
|
||||
}
|
||||
}
|
||||
.forEach { results.add(Suggestion.Catalog(it)) }
|
||||
|
||||
// 4) Création d'un own item si aucune correspondance exacte
|
||||
val exact = results.any { it.label.equals(query, ignoreCase = true) }
|
||||
@ -184,6 +209,7 @@ class ListDetailViewModel @Inject constructor(
|
||||
results.add(0, Suggestion.Create(query))
|
||||
}
|
||||
results
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
||||
|
||||
// ── Actions ─────────────────────────────────────────────────────────────
|
||||
@ -203,6 +229,7 @@ class ListDetailViewModel @Inject constructor(
|
||||
fun applySuggestion(suggestion: Suggestion) {
|
||||
when (suggestion) {
|
||||
is Suggestion.Catalog -> addCatalogItem(suggestion.item)
|
||||
is Suggestion.RoomCatalog -> addCatalogItem(suggestion.item)
|
||||
is Suggestion.Recent -> restoreItem(suggestion.item.id)
|
||||
is Suggestion.Active -> { /* déjà sur la liste, ne fait rien */ }
|
||||
is Suggestion.Create -> addCustomItem(suggestion.rawText)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
MAJOR=1
|
||||
MINOR=22
|
||||
MINOR=24
|
||||
PATCH=0
|
||||
CODE=33
|
||||
CODE=35
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user