feat: add custom emoji field to shopping list items, expand catalog with 200+ items across 12 categories, and implement icon picker in item detail sheet
- Add `customEmoji` field to `ShoppingListItemEntity` for user-selected icons - Increment database version to 4 - Reorganize catalog categories: add "Condiments & Épices", "Snacks & Bonbons", "Maison & Jardin" - Expand catalog from ~50 to 200+ items with comprehensive product coverage across all categories - Add detail tags (Urgent, Offre, Quand cela convient) and
This commit is contained in:
parent
a9eb582c93
commit
b68212b99c
@ -21,7 +21,7 @@ import com.safebite.app.data.local.database.entity.UserProfileEntity
|
|||||||
ShoppingListEntity::class,
|
ShoppingListEntity::class,
|
||||||
ShoppingListItemEntity::class
|
ShoppingListItemEntity::class
|
||||||
],
|
],
|
||||||
version = 3,
|
version = 4,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(Converters::class)
|
||||||
|
|||||||
@ -98,5 +98,6 @@ data class ShoppingListItemEntity(
|
|||||||
val safetyStatus: String? = null, // "SAFE", "WARNING", "DANGER"
|
val safetyStatus: String? = null, // "SAFE", "WARNING", "DANGER"
|
||||||
val allergenWarning: String? = null, // Allergène détecté pour alerte
|
val allergenWarning: String? = null, // Allergène détecté pour alerte
|
||||||
val note: String? = null, // Quantité / description libre (ex: "2 kg")
|
val note: String? = null, // Quantité / description libre (ex: "2 kg")
|
||||||
|
val customEmoji: String? = null, // Emoji personnalisé choisi par l'utilisateur
|
||||||
val addedAt: Long = System.currentTimeMillis()
|
val addedAt: Long = System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
|
|||||||
@ -36,12 +36,15 @@ class CatalogProvider @Inject constructor() {
|
|||||||
"Produits laitiers",
|
"Produits laitiers",
|
||||||
"Boucherie",
|
"Boucherie",
|
||||||
"Épicerie",
|
"Épicerie",
|
||||||
"Boissons",
|
"Condiments & Épices",
|
||||||
"Surgelés",
|
"Surgelés",
|
||||||
|
"Snacks & Bonbons",
|
||||||
|
"Boissons",
|
||||||
"Hygiène",
|
"Hygiène",
|
||||||
"Entretien",
|
"Entretien",
|
||||||
"Bébé",
|
"Bébé",
|
||||||
"Animaux"
|
"Animaux",
|
||||||
|
"Maison & Jardin"
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Liste plate du catalogue. */
|
/** Liste plate du catalogue. */
|
||||||
@ -66,6 +69,41 @@ class CatalogProvider @Inject constructor() {
|
|||||||
add(CatalogItem("Pomme de terre", "Fruits & Légumes", "🥔", listOf("patate")))
|
add(CatalogItem("Pomme de terre", "Fruits & Légumes", "🥔", listOf("patate")))
|
||||||
add(CatalogItem("Champignon", "Fruits & Légumes", "🍄"))
|
add(CatalogItem("Champignon", "Fruits & Légumes", "🍄"))
|
||||||
add(CatalogItem("Épinard", "Fruits & Légumes", "🥬"))
|
add(CatalogItem("Épinard", "Fruits & Légumes", "🥬"))
|
||||||
|
add(CatalogItem("Ananas", "Fruits & Légumes", "🍍"))
|
||||||
|
add(CatalogItem("Pêche", "Fruits & Légumes", "🍑"))
|
||||||
|
add(CatalogItem("Cerise", "Fruits & Légumes", "🍒"))
|
||||||
|
add(CatalogItem("Kiwi", "Fruits & Légumes", "🥝"))
|
||||||
|
add(CatalogItem("Mangue", "Fruits & Légumes", "🥭"))
|
||||||
|
add(CatalogItem("Melon", "Fruits & Légumes", "🍈"))
|
||||||
|
add(CatalogItem("Pastèque", "Fruits & Légumes", "🍉"))
|
||||||
|
add(CatalogItem("Noix de coco", "Fruits & Légumes", "🥥"))
|
||||||
|
add(CatalogItem("Aubergine", "Fruits & Légumes", "🍆"))
|
||||||
|
add(CatalogItem("Maïs", "Fruits & Légumes", "🌽"))
|
||||||
|
add(CatalogItem("Piment", "Fruits & Légumes", "🌶️"))
|
||||||
|
add(CatalogItem("Courgette", "Fruits & Légumes", "🥒"))
|
||||||
|
add(CatalogItem("Chou-fleur", "Fruits & Légumes", "🥦"))
|
||||||
|
add(CatalogItem("Chou", "Fruits & Légumes", "🥬"))
|
||||||
|
add(CatalogItem("Betterave", "Fruits & Légumes", "🫐"))
|
||||||
|
add(CatalogItem("Radis", "Fruits & Légumes", "🥕"))
|
||||||
|
add(CatalogItem("Navet", "Fruits & Légumes", "🥕"))
|
||||||
|
add(CatalogItem("Poireau", "Fruits & Légumes", "🥬"))
|
||||||
|
add(CatalogItem("Céleri", "Fruits & Légumes", "🥬"))
|
||||||
|
add(CatalogItem("Haricots verts", "Fruits & Légumes", "🫛"))
|
||||||
|
add(CatalogItem("Petits pois", "Fruits & Légumes", "🫛"))
|
||||||
|
add(CatalogItem("Artichaut", "Fruits & Légumes", "🥬"))
|
||||||
|
add(CatalogItem("Asperge", "Fruits & Légumes", "🥬"))
|
||||||
|
add(CatalogItem("Basilic", "Fruits & Légumes", "🌿"))
|
||||||
|
add(CatalogItem("Persil", "Fruits & Légumes", "🌿"))
|
||||||
|
add(CatalogItem("Coriandre", "Fruits & Légumes", "🌿"))
|
||||||
|
add(CatalogItem("Menthe", "Fruits & Légumes", "🌿"))
|
||||||
|
add(CatalogItem("Myrtilles", "Fruits & Légumes", "🫐"))
|
||||||
|
add(CatalogItem("Framboises", "Fruits & Légumes", "🍓"))
|
||||||
|
add(CatalogItem("Mûres", "Fruits & Légumes", "🫐"))
|
||||||
|
add(CatalogItem("Abricot", "Fruits & Légumes", "🍑"))
|
||||||
|
add(CatalogItem("Prune", "Fruits & Légumes", "🍑"))
|
||||||
|
add(CatalogItem("Figue", "Fruits & Légumes", "🍇"))
|
||||||
|
add(CatalogItem("Datte", "Fruits & Légumes", "🍇"))
|
||||||
|
add(CatalogItem("Grenade", "Fruits & Légumes", "🍎"))
|
||||||
|
|
||||||
// Boulangerie
|
// Boulangerie
|
||||||
add(CatalogItem("Pain", "Boulangerie", "🍞", listOf("baguette")))
|
add(CatalogItem("Pain", "Boulangerie", "🍞", listOf("baguette")))
|
||||||
@ -75,16 +113,44 @@ class CatalogProvider @Inject constructor() {
|
|||||||
add(CatalogItem("Pain de mie", "Boulangerie", "🍞"))
|
add(CatalogItem("Pain de mie", "Boulangerie", "🍞"))
|
||||||
add(CatalogItem("Biscotte", "Boulangerie", "🍞"))
|
add(CatalogItem("Biscotte", "Boulangerie", "🍞"))
|
||||||
add(CatalogItem("Tortillas", "Boulangerie", "🌯"))
|
add(CatalogItem("Tortillas", "Boulangerie", "🌯"))
|
||||||
|
add(CatalogItem("Pain complet", "Boulangerie", "🍞"))
|
||||||
|
add(CatalogItem("Pain aux céréales", "Boulangerie", "🍞"))
|
||||||
|
add(CatalogItem("Pain de seigle", "Boulangerie", "🍞"))
|
||||||
|
add(CatalogItem("Bagel", "Boulangerie", "🥯"))
|
||||||
|
add(CatalogItem("Muffin", "Boulangerie", "🧁"))
|
||||||
|
add(CatalogItem("Donut", "Boulangerie", "🍩"))
|
||||||
|
add(CatalogItem("Pain au chocolat", "Boulangerie", "🥐"))
|
||||||
|
add(CatalogItem("Chausson aux pommes", "Boulangerie", "🥐"))
|
||||||
|
add(CatalogItem("Éclair", "Boulangerie", "🍰"))
|
||||||
|
add(CatalogItem("Tarte", "Boulangerie", "🥧"))
|
||||||
|
add(CatalogItem("Gâteau", "Boulangerie", "🍰"))
|
||||||
|
|
||||||
// Produits laitiers
|
// Produits laitiers
|
||||||
add(CatalogItem("Lait", "Produits laitiers", "🥛", listOf("milk")))
|
add(CatalogItem("Lait", "Produits laitiers", "🥛", listOf("milk")))
|
||||||
add(CatalogItem("Yaourt", "Produits laitiers", "🥣", listOf("yogurt")))
|
add(CatalogItem("Yaourt", "Produits laitiers", "🥣", listOf("yogurt")))
|
||||||
add(CatalogItem("Beurre", "Produits laitiers", "🧈"))
|
add(CatalogItem("Beurre", "Produits laitiers", "🧈"))
|
||||||
add(CatalogItem("Fromage", "Produits laitiers", "🧀", listOf("cheese")))
|
add(CatalogItem("Fromage", "Produits laitiers", "🧀", listOf("cheese")))
|
||||||
add(CatalogItem("Crème", "Produits laitiers", "🥛"))
|
add(CatalogItem("Crème fraîche", "Produits laitiers", "🥛"))
|
||||||
add(CatalogItem("Œufs", "Produits laitiers", "🥚", listOf("oeufs", "eggs")))
|
add(CatalogItem("Œufs", "Produits laitiers", "🥚", listOf("oeufs", "eggs")))
|
||||||
add(CatalogItem("Mozzarella", "Produits laitiers", "🧀"))
|
add(CatalogItem("Mozzarella", "Produits laitiers", "🧀"))
|
||||||
add(CatalogItem("Parmesan", "Produits laitiers", "🧀"))
|
add(CatalogItem("Parmesan", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Cheddar", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Emmental", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Camembert", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Brie", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Chèvre", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Roquefort", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Gorgonzola", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Feta", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Ricotta", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Mascarpone", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Fromage blanc", "Produits laitiers", "🥛"))
|
||||||
|
add(CatalogItem("Cottage cheese", "Produits laitiers", "🧀"))
|
||||||
|
add(CatalogItem("Crème liquide", "Produits laitiers", "🥛"))
|
||||||
|
add(CatalogItem("Lait concentré", "Produits laitiers", "🥛"))
|
||||||
|
add(CatalogItem("Lait de soja", "Produits laitiers", "🥛"))
|
||||||
|
add(CatalogItem("Lait d'amande", "Produits laitiers", "🥛"))
|
||||||
|
add(CatalogItem("Margarine", "Produits laitiers", "🧈"))
|
||||||
|
|
||||||
// Boucherie
|
// Boucherie
|
||||||
add(CatalogItem("Poulet", "Boucherie", "🍗"))
|
add(CatalogItem("Poulet", "Boucherie", "🍗"))
|
||||||
@ -96,6 +162,27 @@ class CatalogProvider @Inject constructor() {
|
|||||||
add(CatalogItem("Bacon", "Boucherie", "🥓"))
|
add(CatalogItem("Bacon", "Boucherie", "🥓"))
|
||||||
add(CatalogItem("Saumon", "Boucherie", "🐟"))
|
add(CatalogItem("Saumon", "Boucherie", "🐟"))
|
||||||
add(CatalogItem("Thon", "Boucherie", "🐟"))
|
add(CatalogItem("Thon", "Boucherie", "🐟"))
|
||||||
|
add(CatalogItem("Dinde", "Boucherie", "🦃"))
|
||||||
|
add(CatalogItem("Canard", "Boucherie", "🦆"))
|
||||||
|
add(CatalogItem("Agneau", "Boucherie", "🥩"))
|
||||||
|
add(CatalogItem("Veau", "Boucherie", "🥩"))
|
||||||
|
add(CatalogItem("Côtelette", "Boucherie", "🥩"))
|
||||||
|
add(CatalogItem("Rôti", "Boucherie", "🥩"))
|
||||||
|
add(CatalogItem("Merguez", "Boucherie", "🌭"))
|
||||||
|
add(CatalogItem("Chorizo", "Boucherie", "🌭"))
|
||||||
|
add(CatalogItem("Salami", "Boucherie", "🥓"))
|
||||||
|
add(CatalogItem("Saucisson", "Boucherie", "🥓"))
|
||||||
|
add(CatalogItem("Pâté", "Boucherie", "🥓"))
|
||||||
|
add(CatalogItem("Truite", "Boucherie", "🐟"))
|
||||||
|
add(CatalogItem("Cabillaud", "Boucherie", "🐟"))
|
||||||
|
add(CatalogItem("Dorade", "Boucherie", "🐟"))
|
||||||
|
add(CatalogItem("Bar", "Boucherie", "🐟"))
|
||||||
|
add(CatalogItem("Crevettes", "Boucherie", "🦐"))
|
||||||
|
add(CatalogItem("Moules", "Boucherie", "🦪"))
|
||||||
|
add(CatalogItem("Huîtres", "Boucherie", "🦪"))
|
||||||
|
add(CatalogItem("Calamar", "Boucherie", "🦑"))
|
||||||
|
add(CatalogItem("Crabe", "Boucherie", "🦀"))
|
||||||
|
add(CatalogItem("Homard", "Boucherie", "🦞"))
|
||||||
|
|
||||||
// Épicerie
|
// Épicerie
|
||||||
add(CatalogItem("Riz", "Épicerie", "🍚"))
|
add(CatalogItem("Riz", "Épicerie", "🍚"))
|
||||||
@ -104,11 +191,9 @@ class CatalogProvider @Inject constructor() {
|
|||||||
add(CatalogItem("Farine", "Épicerie", "🌾"))
|
add(CatalogItem("Farine", "Épicerie", "🌾"))
|
||||||
add(CatalogItem("Sucre", "Épicerie", "🍬"))
|
add(CatalogItem("Sucre", "Épicerie", "🍬"))
|
||||||
add(CatalogItem("Sel", "Épicerie", "🧂"))
|
add(CatalogItem("Sel", "Épicerie", "🧂"))
|
||||||
|
add(CatalogItem("Huile", "Épicerie", "🫒"))
|
||||||
add(CatalogItem("Huile d'olive", "Épicerie", "🫒"))
|
add(CatalogItem("Huile d'olive", "Épicerie", "🫒"))
|
||||||
add(CatalogItem("Vinaigre", "Épicerie", "🧴"))
|
add(CatalogItem("Vinaigre", "Épicerie", "🧴"))
|
||||||
add(CatalogItem("Moutarde", "Épicerie", "🟡"))
|
|
||||||
add(CatalogItem("Ketchup", "Épicerie", "🍅"))
|
|
||||||
add(CatalogItem("Mayonnaise", "Épicerie", "🥚"))
|
|
||||||
add(CatalogItem("Confiture", "Épicerie", "🍓"))
|
add(CatalogItem("Confiture", "Épicerie", "🍓"))
|
||||||
add(CatalogItem("Miel", "Épicerie", "🍯"))
|
add(CatalogItem("Miel", "Épicerie", "🍯"))
|
||||||
add(CatalogItem("Chocolat", "Épicerie", "🍫"))
|
add(CatalogItem("Chocolat", "Épicerie", "🍫"))
|
||||||
@ -116,18 +201,56 @@ class CatalogProvider @Inject constructor() {
|
|||||||
add(CatalogItem("Céréales", "Épicerie", "🥣", listOf("muesli")))
|
add(CatalogItem("Céréales", "Épicerie", "🥣", listOf("muesli")))
|
||||||
add(CatalogItem("Lentilles", "Épicerie", "🫘"))
|
add(CatalogItem("Lentilles", "Épicerie", "🫘"))
|
||||||
add(CatalogItem("Pois chiches", "Épicerie", "🫘"))
|
add(CatalogItem("Pois chiches", "Épicerie", "🫘"))
|
||||||
|
add(CatalogItem("Haricots rouges", "Épicerie", "🫘"))
|
||||||
|
add(CatalogItem("Haricots blancs", "Épicerie", "🫘"))
|
||||||
add(CatalogItem("Conserves", "Épicerie", "🥫"))
|
add(CatalogItem("Conserves", "Épicerie", "🥫"))
|
||||||
add(CatalogItem("Soupe", "Épicerie", "🍲"))
|
add(CatalogItem("Soupe", "Épicerie", "🍲"))
|
||||||
|
add(CatalogItem("Riz basmati", "Épicerie", "🍚"))
|
||||||
|
add(CatalogItem("Quinoa", "Épicerie", "🍚"))
|
||||||
|
add(CatalogItem("Couscous", "Épicerie", "🍚"))
|
||||||
|
add(CatalogItem("Boulgour", "Épicerie", "🍚"))
|
||||||
|
add(CatalogItem("Polenta", "Épicerie", "🌽"))
|
||||||
|
add(CatalogItem("Flocons d'avoine", "Épicerie", "🥣"))
|
||||||
|
add(CatalogItem("Muesli", "Épicerie", "🥣"))
|
||||||
|
add(CatalogItem("Granola", "Épicerie", "🥣"))
|
||||||
|
add(CatalogItem("Pâte à tartiner", "Épicerie", "🍫"))
|
||||||
|
add(CatalogItem("Beurre de cacahuète", "Épicerie", "🥜"))
|
||||||
|
add(CatalogItem("Sirop d'érable", "Épicerie", "🍯"))
|
||||||
|
add(CatalogItem("Levure", "Épicerie", "🧂"))
|
||||||
|
add(CatalogItem("Bicarbonate", "Épicerie", "🧂"))
|
||||||
|
add(CatalogItem("Sucre vanillé", "Épicerie", "🍬"))
|
||||||
|
add(CatalogItem("Cacao", "Épicerie", "🍫"))
|
||||||
|
add(CatalogItem("Thon en boîte", "Épicerie", "🥫"))
|
||||||
|
add(CatalogItem("Sardines", "Épicerie", "🥫"))
|
||||||
|
add(CatalogItem("Maquereau", "Épicerie", "🥫"))
|
||||||
|
add(CatalogItem("Sauce tomate", "Épicerie", "🍅"))
|
||||||
|
add(CatalogItem("Concentré de tomate", "Épicerie", "🍅"))
|
||||||
|
add(CatalogItem("Tomates pelées", "Épicerie", "🥫"))
|
||||||
|
|
||||||
// Boissons
|
// Condiments & Épices
|
||||||
add(CatalogItem("Eau", "Boissons", "💧"))
|
add(CatalogItem("Moutarde", "Condiments & Épices", "🟡"))
|
||||||
add(CatalogItem("Jus d'orange", "Boissons", "🧃"))
|
add(CatalogItem("Ketchup", "Condiments & Épices", "🍅"))
|
||||||
add(CatalogItem("Café", "Boissons", "☕"))
|
add(CatalogItem("Mayonnaise", "Condiments & Épices", "🥚"))
|
||||||
add(CatalogItem("Thé", "Boissons", "🍵"))
|
add(CatalogItem("Poivre", "Condiments & Épices", "🧂"))
|
||||||
add(CatalogItem("Vin", "Boissons", "🍷"))
|
add(CatalogItem("Paprika", "Condiments & Épices", "🌶️"))
|
||||||
add(CatalogItem("Bière", "Boissons", "🍺"))
|
add(CatalogItem("Curry", "Condiments & Épices", "🌶️"))
|
||||||
add(CatalogItem("Soda", "Boissons", "🥤"))
|
add(CatalogItem("Cumin", "Condiments & Épices", "🌶️"))
|
||||||
add(CatalogItem("Limonade", "Boissons", "🍋"))
|
add(CatalogItem("Curcuma", "Condiments & Épices", "🌶️"))
|
||||||
|
add(CatalogItem("Gingembre", "Condiments & Épices", "🌶️"))
|
||||||
|
add(CatalogItem("Cannelle", "Condiments & Épices", "🌶️"))
|
||||||
|
add(CatalogItem("Muscade", "Condiments & Épices", "🌶️"))
|
||||||
|
add(CatalogItem("Thym", "Condiments & Épices", "🌿"))
|
||||||
|
add(CatalogItem("Romarin", "Condiments & Épices", "🌿"))
|
||||||
|
add(CatalogItem("Origan", "Condiments & Épices", "🌿"))
|
||||||
|
add(CatalogItem("Laurier", "Condiments & Épices", "🌿"))
|
||||||
|
add(CatalogItem("Herbes de Provence", "Condiments & Épices", "🌿"))
|
||||||
|
add(CatalogItem("Sauce soja", "Condiments & Épices", "🧴"))
|
||||||
|
add(CatalogItem("Vinaigre balsamique", "Condiments & Épices", "🧴"))
|
||||||
|
add(CatalogItem("Tabasco", "Condiments & Épices", "🌶️"))
|
||||||
|
add(CatalogItem("Harissa", "Condiments & Épices", "🌶️"))
|
||||||
|
add(CatalogItem("Wasabi", "Condiments & Épices", "🌶️"))
|
||||||
|
add(CatalogItem("Pesto", "Condiments & Épices", "🌿"))
|
||||||
|
add(CatalogItem("Tapenade", "Condiments & Épices", "🫒"))
|
||||||
|
|
||||||
// Surgelés
|
// Surgelés
|
||||||
add(CatalogItem("Pizza surgelée", "Surgelés", "🍕"))
|
add(CatalogItem("Pizza surgelée", "Surgelés", "🍕"))
|
||||||
@ -135,33 +258,142 @@ class CatalogProvider @Inject constructor() {
|
|||||||
add(CatalogItem("Glace", "Surgelés", "🍨"))
|
add(CatalogItem("Glace", "Surgelés", "🍨"))
|
||||||
add(CatalogItem("Légumes surgelés", "Surgelés", "🥦"))
|
add(CatalogItem("Légumes surgelés", "Surgelés", "🥦"))
|
||||||
add(CatalogItem("Poisson surgelé", "Surgelés", "🐟"))
|
add(CatalogItem("Poisson surgelé", "Surgelés", "🐟"))
|
||||||
|
add(CatalogItem("Crevettes surgelées", "Surgelés", "🦐"))
|
||||||
|
add(CatalogItem("Fruits surgelés", "Surgelés", "🍓"))
|
||||||
|
add(CatalogItem("Plat préparé", "Surgelés", "🍱"))
|
||||||
|
add(CatalogItem("Lasagnes", "Surgelés", "🍝"))
|
||||||
|
add(CatalogItem("Nuggets", "Surgelés", "🍗"))
|
||||||
|
add(CatalogItem("Poisson pané", "Surgelés", "🐟"))
|
||||||
|
add(CatalogItem("Sorbet", "Surgelés", "🍧"))
|
||||||
|
add(CatalogItem("Gâteau glacé", "Surgelés", "🍰"))
|
||||||
|
|
||||||
|
// Snacks & Bonbons
|
||||||
|
add(CatalogItem("Chips", "Snacks & Bonbons", "🥔"))
|
||||||
|
add(CatalogItem("Cacahuètes", "Snacks & Bonbons", "🥜"))
|
||||||
|
add(CatalogItem("Noix", "Snacks & Bonbons", "🌰"))
|
||||||
|
add(CatalogItem("Amandes", "Snacks & Bonbons", "🌰"))
|
||||||
|
add(CatalogItem("Noisettes", "Snacks & Bonbons", "🌰"))
|
||||||
|
add(CatalogItem("Pistaches", "Snacks & Bonbons", "🥜"))
|
||||||
|
add(CatalogItem("Noix de cajou", "Snacks & Bonbons", "🥜"))
|
||||||
|
add(CatalogItem("Pop-corn", "Snacks & Bonbons", "🍿"))
|
||||||
|
add(CatalogItem("Bonbons", "Snacks & Bonbons", "🍬"))
|
||||||
|
add(CatalogItem("Chewing-gum", "Snacks & Bonbons", "🍬"))
|
||||||
|
add(CatalogItem("Chocolat noir", "Snacks & Bonbons", "🍫"))
|
||||||
|
add(CatalogItem("Chocolat au lait", "Snacks & Bonbons", "🍫"))
|
||||||
|
add(CatalogItem("Barres chocolatées", "Snacks & Bonbons", "🍫"))
|
||||||
|
add(CatalogItem("Barres de céréales", "Snacks & Bonbons", "🥣"))
|
||||||
|
add(CatalogItem("Fruits secs", "Snacks & Bonbons", "🍇"))
|
||||||
|
add(CatalogItem("Raisins secs", "Snacks & Bonbons", "🍇"))
|
||||||
|
|
||||||
|
// Boissons
|
||||||
|
add(CatalogItem("Eau", "Boissons", "💧"))
|
||||||
|
add(CatalogItem("Eau gazeuse", "Boissons", "💧"))
|
||||||
|
add(CatalogItem("Jus d'orange", "Boissons", "🧃"))
|
||||||
|
add(CatalogItem("Jus de pomme", "Boissons", "🧃"))
|
||||||
|
add(CatalogItem("Jus de raisin", "Boissons", "🧃"))
|
||||||
|
add(CatalogItem("Jus multivitaminé", "Boissons", "🧃"))
|
||||||
|
add(CatalogItem("Café", "Boissons", "☕"))
|
||||||
|
add(CatalogItem("Café moulu", "Boissons", "☕"))
|
||||||
|
add(CatalogItem("Café en grains", "Boissons", "☕"))
|
||||||
|
add(CatalogItem("Café soluble", "Boissons", "☕"))
|
||||||
|
add(CatalogItem("Thé", "Boissons", "🍵"))
|
||||||
|
add(CatalogItem("Thé vert", "Boissons", "🍵"))
|
||||||
|
add(CatalogItem("Thé noir", "Boissons", "🍵"))
|
||||||
|
add(CatalogItem("Infusion", "Boissons", "🍵"))
|
||||||
|
add(CatalogItem("Chocolat chaud", "Boissons", "☕"))
|
||||||
|
add(CatalogItem("Vin rouge", "Boissons", "🍷"))
|
||||||
|
add(CatalogItem("Vin blanc", "Boissons", "🍷"))
|
||||||
|
add(CatalogItem("Vin rosé", "Boissons", "🍷"))
|
||||||
|
add(CatalogItem("Champagne", "Boissons", "🍾"))
|
||||||
|
add(CatalogItem("Bière", "Boissons", "🍺"))
|
||||||
|
add(CatalogItem("Bière blonde", "Boissons", "🍺"))
|
||||||
|
add(CatalogItem("Bière brune", "Boissons", "🍺"))
|
||||||
|
add(CatalogItem("Cidre", "Boissons", "🍺"))
|
||||||
|
add(CatalogItem("Soda", "Boissons", "🥤"))
|
||||||
|
add(CatalogItem("Cola", "Boissons", "🥤"))
|
||||||
|
add(CatalogItem("Limonade", "Boissons", "🍋"))
|
||||||
|
add(CatalogItem("Orangina", "Boissons", "🍊"))
|
||||||
|
add(CatalogItem("Sirop", "Boissons", "🧃"))
|
||||||
|
add(CatalogItem("Smoothie", "Boissons", "🥤"))
|
||||||
|
add(CatalogItem("Boisson énergisante", "Boissons", "🥤"))
|
||||||
|
|
||||||
// Hygiène
|
// Hygiène
|
||||||
add(CatalogItem("Papier toilette", "Hygiène", "🧻", listOf("toilet paper")))
|
add(CatalogItem("Papier toilette", "Hygiène", "🧻", listOf("toilet paper")))
|
||||||
add(CatalogItem("Mouchoirs", "Hygiène", "🤧"))
|
add(CatalogItem("Mouchoirs", "Hygiène", "🤧"))
|
||||||
add(CatalogItem("Dentifrice", "Hygiène", "🦷"))
|
add(CatalogItem("Dentifrice", "Hygiène", "🦷"))
|
||||||
|
add(CatalogItem("Brosse à dents", "Hygiène", "🪥"))
|
||||||
|
add(CatalogItem("Fil dentaire", "Hygiène", "🦷"))
|
||||||
|
add(CatalogItem("Bain de bouche", "Hygiène", "🦷"))
|
||||||
add(CatalogItem("Shampoing", "Hygiène", "🧴"))
|
add(CatalogItem("Shampoing", "Hygiène", "🧴"))
|
||||||
|
add(CatalogItem("Après-shampoing", "Hygiène", "🧴"))
|
||||||
add(CatalogItem("Savon", "Hygiène", "🧼"))
|
add(CatalogItem("Savon", "Hygiène", "🧼"))
|
||||||
add(CatalogItem("Gel douche", "Hygiène", "🧴"))
|
add(CatalogItem("Gel douche", "Hygiène", "🧴"))
|
||||||
add(CatalogItem("Déodorant", "Hygiène", "🧴"))
|
add(CatalogItem("Déodorant", "Hygiène", "🧴"))
|
||||||
|
add(CatalogItem("Parfum", "Hygiène", "🧴"))
|
||||||
|
add(CatalogItem("Crème hydratante", "Hygiène", "🧴"))
|
||||||
|
add(CatalogItem("Crème solaire", "Hygiène", "🧴"))
|
||||||
|
add(CatalogItem("Rasoir", "Hygiène", "🪒"))
|
||||||
|
add(CatalogItem("Mousse à raser", "Hygiène", "🧴"))
|
||||||
|
add(CatalogItem("Coton-tige", "Hygiène", "🧻"))
|
||||||
|
add(CatalogItem("Coton", "Hygiène", "🧻"))
|
||||||
|
add(CatalogItem("Serviettes hygiéniques", "Hygiène", "🧻"))
|
||||||
|
add(CatalogItem("Tampons", "Hygiène", "🧻"))
|
||||||
|
|
||||||
// Entretien
|
// Entretien
|
||||||
add(CatalogItem("Lessive", "Entretien", "🧺"))
|
add(CatalogItem("Lessive", "Entretien", "🧺"))
|
||||||
|
add(CatalogItem("Adoucissant", "Entretien", "🧴"))
|
||||||
add(CatalogItem("Liquide vaisselle", "Entretien", "🧴"))
|
add(CatalogItem("Liquide vaisselle", "Entretien", "🧴"))
|
||||||
|
add(CatalogItem("Tablettes lave-vaisselle", "Entretien", "🧴"))
|
||||||
add(CatalogItem("Éponge", "Entretien", "🧽"))
|
add(CatalogItem("Éponge", "Entretien", "🧽"))
|
||||||
add(CatalogItem("Javel", "Entretien", "🧴"))
|
add(CatalogItem("Javel", "Entretien", "🧴"))
|
||||||
|
add(CatalogItem("Nettoyant multi-usage", "Entretien", "🧴"))
|
||||||
|
add(CatalogItem("Nettoyant vitres", "Entretien", "🧴"))
|
||||||
|
add(CatalogItem("Nettoyant sol", "Entretien", "🧴"))
|
||||||
|
add(CatalogItem("Nettoyant WC", "Entretien", "🧴"))
|
||||||
add(CatalogItem("Sacs poubelle", "Entretien", "🗑️"))
|
add(CatalogItem("Sacs poubelle", "Entretien", "🗑️"))
|
||||||
|
add(CatalogItem("Essuie-tout", "Entretien", "🧻"))
|
||||||
|
add(CatalogItem("Serpillière", "Entretien", "🧹"))
|
||||||
|
add(CatalogItem("Balai", "Entretien", "🧹"))
|
||||||
|
add(CatalogItem("Pelle", "Entretien", "🧹"))
|
||||||
|
|
||||||
// Bébé
|
// Bébé
|
||||||
add(CatalogItem("Couches", "Bébé", "👶"))
|
add(CatalogItem("Couches", "Bébé", "👶"))
|
||||||
add(CatalogItem("Lait infantile", "Bébé", "🍼"))
|
add(CatalogItem("Lait infantile", "Bébé", "🍼"))
|
||||||
add(CatalogItem("Compote bébé", "Bébé", "🍎"))
|
add(CatalogItem("Compote bébé", "Bébé", "🍎"))
|
||||||
add(CatalogItem("Lingettes bébé", "Bébé", "🧻"))
|
add(CatalogItem("Lingettes bébé", "Bébé", "🧻"))
|
||||||
|
add(CatalogItem("Petits pots", "Bébé", "🍼"))
|
||||||
|
add(CatalogItem("Céréales bébé", "Bébé", "🥣"))
|
||||||
|
add(CatalogItem("Biscuits bébé", "Bébé", "🍪"))
|
||||||
|
add(CatalogItem("Biberon", "Bébé", "🍼"))
|
||||||
|
add(CatalogItem("Tétine", "Bébé", "🍼"))
|
||||||
|
add(CatalogItem("Crème pour le change", "Bébé", "🧴"))
|
||||||
|
add(CatalogItem("Savon bébé", "Bébé", "🧼"))
|
||||||
|
add(CatalogItem("Shampoing bébé", "Bébé", "🧴"))
|
||||||
|
|
||||||
// Animaux
|
// Animaux
|
||||||
add(CatalogItem("Croquettes chien", "Animaux", "🐶"))
|
add(CatalogItem("Croquettes chien", "Animaux", "🐶"))
|
||||||
add(CatalogItem("Croquettes chat", "Animaux", "🐱"))
|
add(CatalogItem("Croquettes chat", "Animaux", "🐱"))
|
||||||
|
add(CatalogItem("Pâtée chien", "Animaux", "🐶"))
|
||||||
add(CatalogItem("Pâtée chat", "Animaux", "🐈"))
|
add(CatalogItem("Pâtée chat", "Animaux", "🐈"))
|
||||||
|
add(CatalogItem("Friandises chien", "Animaux", "🦴"))
|
||||||
|
add(CatalogItem("Friandises chat", "Animaux", "🐟"))
|
||||||
|
add(CatalogItem("Litière", "Animaux", "🐈"))
|
||||||
|
add(CatalogItem("Jouets pour animaux", "Animaux", "🎾"))
|
||||||
|
add(CatalogItem("Shampoing animal", "Animaux", "🧴"))
|
||||||
|
|
||||||
|
// Maison & Jardin
|
||||||
|
add(CatalogItem("Ampoules", "Maison & Jardin", "💡"))
|
||||||
|
add(CatalogItem("Piles", "Maison & Jardin", "🔋"))
|
||||||
|
add(CatalogItem("Allumettes", "Maison & Jardin", "🔥"))
|
||||||
|
add(CatalogItem("Bougies", "Maison & Jardin", "🕯️"))
|
||||||
|
add(CatalogItem("Papier aluminium", "Maison & Jardin", "📦"))
|
||||||
|
add(CatalogItem("Film alimentaire", "Maison & Jardin", "📦"))
|
||||||
|
add(CatalogItem("Papier cuisson", "Maison & Jardin", "📦"))
|
||||||
|
add(CatalogItem("Sacs congélation", "Maison & Jardin", "📦"))
|
||||||
|
add(CatalogItem("Terreau", "Maison & Jardin", "🌱"))
|
||||||
|
add(CatalogItem("Engrais", "Maison & Jardin", "🌱"))
|
||||||
|
add(CatalogItem("Graines", "Maison & Jardin", "🌱"))
|
||||||
|
add(CatalogItem("Pots de fleurs", "Maison & Jardin", "🪴"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Items pour une catégorie donnée (ordre catalogue). */
|
/** Items pour une catégorie donnée (ordre catalogue). */
|
||||||
@ -211,12 +443,15 @@ class CatalogProvider @Inject constructor() {
|
|||||||
"Produits laitiers" -> "🥛"
|
"Produits laitiers" -> "🥛"
|
||||||
"Boucherie" -> "🥩"
|
"Boucherie" -> "🥩"
|
||||||
"Épicerie" -> "🛒"
|
"Épicerie" -> "🛒"
|
||||||
"Boissons" -> "🥤"
|
"Condiments & Épices" -> "🌶️"
|
||||||
"Surgelés" -> "🧊"
|
"Surgelés" -> "🧊"
|
||||||
|
"Snacks & Bonbons" -> "🍿"
|
||||||
|
"Boissons" -> "🥤"
|
||||||
"Hygiène" -> "🧴"
|
"Hygiène" -> "🧴"
|
||||||
"Entretien" -> "🧹"
|
"Entretien" -> "🧹"
|
||||||
"Bébé" -> "👶"
|
"Bébé" -> "👶"
|
||||||
"Animaux" -> "🐾"
|
"Animaux" -> "🐾"
|
||||||
|
"Maison & Jardin" -> "🏡"
|
||||||
else -> "📦"
|
else -> "📦"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,324 @@
|
|||||||
|
package com.safebite.app.presentation.screen.lists
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import com.safebite.app.domain.engine.CatalogProvider
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun IconPickerSheet(
|
||||||
|
currentEmoji: String,
|
||||||
|
categories: List<String>,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onSelectIcon: (String) -> Unit,
|
||||||
|
catalogProvider: CatalogProvider = hiltViewModel<ListDetailViewModel>().catalog
|
||||||
|
) {
|
||||||
|
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
|
var searchQuery by remember { mutableStateOf("") }
|
||||||
|
val expandedCategories = remember { mutableStateMapOf<String, Boolean>() }
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
sheetState = sheetState
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.navigationBarsPadding()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
|
) {
|
||||||
|
// Header
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
IconButton(onClick = onDismiss) {
|
||||||
|
Icon(Icons.Filled.Close, contentDescription = "Fermer")
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = "Choisir une icône",
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
IconButton(onClick = { onSelectIcon("") }) {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.Delete,
|
||||||
|
contentDescription = "Supprimer l'icône",
|
||||||
|
tint = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current icon display
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 12.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(120.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(MaterialTheme.colorScheme.primaryContainer),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = currentEmoji,
|
||||||
|
style = MaterialTheme.typography.displayLarge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search bar
|
||||||
|
OutlinedTextField(
|
||||||
|
value = searchQuery,
|
||||||
|
onValueChange = { searchQuery = it },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 12.dp),
|
||||||
|
placeholder = { Text("Chercher une icône") },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(Icons.Filled.Search, contentDescription = null)
|
||||||
|
},
|
||||||
|
trailingIcon = {
|
||||||
|
if (searchQuery.isNotEmpty()) {
|
||||||
|
IconButton(onClick = { searchQuery = "" }) {
|
||||||
|
Icon(Icons.Filled.Close, contentDescription = "Effacer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
singleLine = true,
|
||||||
|
shape = RoundedCornerShape(28.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Icon grid by category
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
categories.forEach { category ->
|
||||||
|
val categoryItems = catalogProvider.itemsForCategory(category)
|
||||||
|
val filteredItems = if (searchQuery.isNotBlank()) {
|
||||||
|
categoryItems.filter {
|
||||||
|
it.name.contains(searchQuery, ignoreCase = true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
categoryItems
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filteredItems.isNotEmpty()) {
|
||||||
|
val expanded = expandedCategories[category] ?: (searchQuery.isNotBlank())
|
||||||
|
|
||||||
|
item(key = "header-$category") {
|
||||||
|
CategoryHeader(
|
||||||
|
title = category,
|
||||||
|
count = filteredItems.size,
|
||||||
|
expanded = expanded,
|
||||||
|
onToggle = {
|
||||||
|
expandedCategories[category] = !expanded
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expanded) {
|
||||||
|
item(key = "grid-$category") {
|
||||||
|
IconGrid(
|
||||||
|
items = filteredItems,
|
||||||
|
currentEmoji = currentEmoji,
|
||||||
|
onSelectIcon = onSelectIcon
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CategoryHeader(
|
||||||
|
title: String,
|
||||||
|
count: Int,
|
||||||
|
expanded: Boolean,
|
||||||
|
onToggle: () -> Unit
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(RoundedCornerShape(8.dp))
|
||||||
|
.clickable(onClick = onToggle)
|
||||||
|
.padding(vertical = 12.dp, horizontal = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.KeyboardArrowRight,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.rotate(if (expanded) 90f else 0f),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.KeyboardArrowDown,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun IconGrid(
|
||||||
|
items: List<CatalogProvider.CatalogItem>,
|
||||||
|
currentEmoji: String,
|
||||||
|
onSelectIcon: (String) -> Unit
|
||||||
|
) {
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(3),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(((items.size + 2) / 3 * 100).dp.coerceAtMost(400.dp)),
|
||||||
|
contentPadding = PaddingValues(4.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
items(items) { item ->
|
||||||
|
IconCard(
|
||||||
|
emoji = item.emoji,
|
||||||
|
label = item.name,
|
||||||
|
isSelected = item.emoji == currentEmoji,
|
||||||
|
onClick = { onSelectIcon(item.emoji) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun IconCard(
|
||||||
|
emoji: String,
|
||||||
|
label: String,
|
||||||
|
isSelected: Boolean,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
val backgroundColor = if (isSelected) {
|
||||||
|
MaterialTheme.colorScheme.primaryContainer
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.surfaceVariant
|
||||||
|
}
|
||||||
|
val contentColor = if (isSelected) {
|
||||||
|
MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
}
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.clickable(onClick = onClick),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
colors = CardDefaults.cardColors(containerColor = backgroundColor)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier.padding(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = emoji,
|
||||||
|
style = MaterialTheme.typography.headlineMedium
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
Text(
|
||||||
|
text = label,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = contentColor,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
maxLines = 2,
|
||||||
|
fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Normal
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (isSelected) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Check,
|
||||||
|
contentDescription = "Sélectionné",
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.TopEnd)
|
||||||
|
.padding(4.dp)
|
||||||
|
.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -260,6 +260,7 @@ fun ListDetailScreen(
|
|||||||
onDismiss = viewModel::closeItemDetails,
|
onDismiss = viewModel::closeItemDetails,
|
||||||
onUpdateNote = { note -> viewModel.updateItemNote(selected.id, note) },
|
onUpdateNote = { note -> viewModel.updateItemNote(selected.id, note) },
|
||||||
onUpdateCategory = { cat -> viewModel.updateItemCategory(selected.id, cat) },
|
onUpdateCategory = { cat -> viewModel.updateItemCategory(selected.id, cat) },
|
||||||
|
onUpdateEmoji = { emoji -> viewModel.updateItemEmoji(selected.id, emoji) },
|
||||||
onMoveTo = { targetListId -> viewModel.moveItemToList(selected.id, targetListId) },
|
onMoveTo = { targetListId -> viewModel.moveItemToList(selected.id, targetListId) },
|
||||||
onDelete = { viewModel.deleteItem(selected.id) },
|
onDelete = { viewModel.deleteItem(selected.id) },
|
||||||
onOpenProduct = selected.barcode?.let { bc -> { onOpenProduct(bc) } }
|
onOpenProduct = selected.barcode?.let { bc -> { onOpenProduct(bc) } }
|
||||||
@ -774,6 +775,7 @@ private fun ItemDetailSheet(
|
|||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onUpdateNote: (String) -> Unit,
|
onUpdateNote: (String) -> Unit,
|
||||||
onUpdateCategory: (String) -> Unit,
|
onUpdateCategory: (String) -> Unit,
|
||||||
|
onUpdateEmoji: (String?) -> Unit,
|
||||||
onMoveTo: (Long) -> Unit,
|
onMoveTo: (Long) -> Unit,
|
||||||
onDelete: () -> Unit,
|
onDelete: () -> Unit,
|
||||||
onOpenProduct: (() -> Unit)?
|
onOpenProduct: (() -> Unit)?
|
||||||
@ -781,6 +783,7 @@ private fun ItemDetailSheet(
|
|||||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
var note by remember(item.id) { mutableStateOf(item.note.orEmpty()) }
|
var note by remember(item.id) { mutableStateOf(item.note.orEmpty()) }
|
||||||
var showCategoryPicker by remember { mutableStateOf(false) }
|
var showCategoryPicker by remember { mutableStateOf(false) }
|
||||||
|
var showIconPicker by remember { mutableStateOf(false) }
|
||||||
var showMovePicker by remember { mutableStateOf(false) }
|
var showMovePicker by remember { mutableStateOf(false) }
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
|
|
||||||
@ -835,21 +838,85 @@ private fun ItemDetailSheet(
|
|||||||
maxLines = 3
|
maxLines = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// Catégorie
|
// Détails de l'article
|
||||||
ActionRow(
|
Text(
|
||||||
title = "Section",
|
text = "Détails de l'article pour ${item.productName}",
|
||||||
value = item.category ?: "Autre",
|
style = MaterialTheme.typography.titleSmall,
|
||||||
onClick = { showCategoryPicker = true }
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
modifier = Modifier.padding(top = 8.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Déplacer
|
Row(
|
||||||
if (otherLists.isNotEmpty()) {
|
modifier = Modifier.fillMaxWidth(),
|
||||||
ActionRow(
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
title = "Déplacer vers une autre liste",
|
) {
|
||||||
value = null,
|
DetailTagButton(
|
||||||
onClick = { showMovePicker = true },
|
icon = Icons.Filled.AutoAwesome,
|
||||||
leadingIcon = Icons.Filled.SwapHoriz
|
label = "Urgent",
|
||||||
|
selected = false,
|
||||||
|
onClick = { /* TODO */ },
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
|
DetailTagButton(
|
||||||
|
icon = Icons.Filled.Done,
|
||||||
|
label = "Offre",
|
||||||
|
selected = false,
|
||||||
|
onClick = { /* TODO */ },
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
DetailTagButton(
|
||||||
|
icon = Icons.Filled.History,
|
||||||
|
label = "Quand cela convient",
|
||||||
|
selected = false,
|
||||||
|
onClick = { /* TODO */ },
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paramètres
|
||||||
|
Text(
|
||||||
|
text = "Paramètres",
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
modifier = Modifier.padding(top = 8.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
ParameterButton(
|
||||||
|
icon = Icons.Filled.Done,
|
||||||
|
label = "Changer une icône",
|
||||||
|
onClick = { showIconPicker = true },
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
ParameterButton(
|
||||||
|
icon = Icons.Filled.Camera,
|
||||||
|
label = "Ajouter une photo",
|
||||||
|
onClick = { /* TODO: Photo picker */ },
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
ParameterButton(
|
||||||
|
icon = Icons.Filled.KeyboardArrowDown,
|
||||||
|
label = "Changer une section",
|
||||||
|
onClick = { showCategoryPicker = true },
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
if (otherLists.isNotEmpty()) {
|
||||||
|
ParameterButton(
|
||||||
|
icon = Icons.Filled.SwapHoriz,
|
||||||
|
label = "Déplacer l'article",
|
||||||
|
onClick = { showMovePicker = true },
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ouvrir fiche produit
|
// Ouvrir fiche produit
|
||||||
@ -997,6 +1064,19 @@ private fun ItemDetailSheet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sélecteur d'icône
|
||||||
|
if (showIconPicker) {
|
||||||
|
IconPickerSheet(
|
||||||
|
currentEmoji = item.emoji,
|
||||||
|
categories = categories,
|
||||||
|
onDismiss = { showIconPicker = false },
|
||||||
|
onSelectIcon = { emoji ->
|
||||||
|
onUpdateEmoji(emoji.ifEmpty { null })
|
||||||
|
showIconPicker = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -1044,3 +1124,97 @@ private fun ActionRow(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DetailTagButton(
|
||||||
|
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||||
|
label: String,
|
||||||
|
selected: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val backgroundColor = if (selected) {
|
||||||
|
MaterialTheme.colorScheme.primaryContainer
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.6f)
|
||||||
|
}
|
||||||
|
val contentColor = if (selected) {
|
||||||
|
MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
}
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = modifier
|
||||||
|
.heightIn(min = 56.dp)
|
||||||
|
.clickable(onClick = onClick),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
colors = CardDefaults.cardColors(containerColor = backgroundColor)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = contentColor,
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
Text(
|
||||||
|
text = label,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = contentColor,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ParameterButton(
|
||||||
|
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||||
|
label: String,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = modifier
|
||||||
|
.heightIn(min = 80.dp)
|
||||||
|
.clickable(onClick = onClick),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.6f)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(12.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.size(28.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
Text(
|
||||||
|
text = label,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -322,6 +322,15 @@ class ListDetailViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Change l'emoji personnalisé d'un article. */
|
||||||
|
fun updateItemEmoji(id: Long, emoji: String?) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val listId = _listIdFlow.value
|
||||||
|
val item = manageListUseCase.getItems(listId).firstOrNull { it.id == id } ?: return@launch
|
||||||
|
manageListUseCase.updateItem(item.copy(customEmoji = emoji))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Déplace un article vers une autre liste. */
|
/** Déplace un article vers une autre liste. */
|
||||||
fun moveItemToList(id: Long, targetListId: Long) {
|
fun moveItemToList(id: Long, targetListId: Long) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@ -393,7 +402,7 @@ class ListDetailViewModel @Inject constructor(
|
|||||||
safetyStatus = safetyStatus,
|
safetyStatus = safetyStatus,
|
||||||
allergenWarning = allergenWarning,
|
allergenWarning = allergenWarning,
|
||||||
note = note,
|
note = note,
|
||||||
emoji = catalog.emojiFor(productName, category)
|
emoji = customEmoji ?: catalog.emojiFor(productName, category)
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user