diff --git a/VinEye/CLAUDE.md b/VinEye/CLAUDE.md index 323259b..d60e066 100644 --- a/VinEye/CLAUDE.md +++ b/VinEye/CLAUDE.md @@ -1,7 +1,7 @@ # VinEye -Application mobile React Native (Expo) de détection de cépages par IA. -Analyse la vigne en temps réel via la caméra, identifie le cépage, et gamifie la progression. +Application mobile React Native (Expo) de detection de maladies de la vigne. +Cible des amateurs de vin/jardinage. Scan par camera, identification de maladies, bibliotheque de cepages, gamification. --- @@ -10,17 +10,16 @@ Analyse la vigne en temps réel via la caméra, identifie le cépage, et gamifie | Couche | Technologies | |--------|-------------| | Framework | React Native + Expo SDK 54 (bare workflow) | -| Navigation | React Navigation v7 (NativeStack + BottomTabs) — **PAS Expo Router** | +| Navigation | React Navigation v7 (NativeStack + BottomTabs) | | Langage | TypeScript strict | -| UI | Composants custom (pas de shadcn — RN only) | -| Animations | React Native Reanimated v4 (`useEffect` vient de `react`, **pas** de reanimated) | -| IA | TFLite mock (weighted random : 70% vine / 20% uncertain / 10% not_vine) | -| Persistance | AsyncStorage (`@react-native-async-storage/async-storage`) | +| Styling | **NativeWind v4** (Tailwind) prioritaire, StyleSheet pour ombres/gradients | +| Icones | **lucide-react-native** (bottom bar) + **Ionicons** (reste de l'app) | +| Animations | React Native Reanimated v4 | +| IA | TFLite mock (weighted random) | +| Persistance | AsyncStorage | | i18n | i18next + react-i18next (FR + EN) | -| Caméra | expo-camera | +| Camera | expo-camera | | Haptics | expo-haptics | -| SVG | react-native-svg | -| Lottie | lottie-react-native | | Package manager | **pnpm** | --- @@ -29,29 +28,25 @@ Analyse la vigne en temps réel via la caméra, identifie le cépage, et gamifie ``` VinEye/ -├── App.tsx # Entry point (i18n init + RootNavigator) +├── App.tsx ├── src/ -│ ├── assets/ -│ │ ├── images/ # logo.svg, icon.png, splash.png -│ │ └── lottie/ # confetti.json, scan-success.json, vine-leaf.json, level-up.json │ ├── components/ -│ │ ├── ui/ # Button, Card, Badge, ProgressCircle, AnimatedCounter +│ │ ├── ui/ # Text, Button, Card, Badge, ProgressCircle +│ │ ├── home/ # SearchHeader, SearchSection, HomeCta, FrequentDiseases, +│ │ │ # SeasonAlert, PracticalGuides, statssection, gamificationstat +│ │ │ └── components/ # homeheader (SectionHeader) │ │ ├── scanner/ # DetectionFrame, CameraOverlay, ConfidenceMeter -│ │ ├── gamification/ # XPBar, BadgeCard, LevelIndicator, StreakCounter +│ │ ├── gamification/ # XPBar, BadgeCard, ProgressRing, LevelIndicator │ │ └── history/ # ScanCard, ScanList +│ ├── data/ # diseases.ts (7 maladies), guides.ts (3 guides) │ ├── hooks/ # useDetection, useGameProgress, useHistory │ ├── i18n/ # fr.json, en.json, index.ts │ ├── navigation/ # RootNavigator, BottomTabNavigator, linking.ts -│ ├── screens/ # SplashScreen, HomeScreen, ScannerScreen, ResultScreen, HistoryScreen, ProfileScreen -│ ├── services/ -│ │ ├── tflite/model.ts # Mock TFLite inference -│ │ ├── storage.ts # AsyncStorage wrapper typé -│ │ └── haptics.ts # hapticSuccess/Warning/Error/Light/Medium/Heavy -│ ├── theme/ # colors.ts, typography.ts, spacing.ts, index.ts +│ ├── screens/ # 11 ecrans (voir Navigation) +│ ├── services/ # tflite/model.ts, storage.ts, haptics.ts +│ ├── theme/ # colors.ts, typography.ts, spacing.ts │ ├── types/ # detection.ts, gamification.ts, navigation.ts -│ └── utils/ -│ ├── cepages.ts # 15 cépages (origine, couleur, caractéristiques, régions) -│ └── achievements.ts # XP_REWARDS, LEVELS, BADGE_DEFINITIONS, checkNewBadges, getLevelForXP +│ └── utils/ # cepages.ts, achievements.ts ``` --- @@ -59,71 +54,137 @@ VinEye/ ## Navigation ``` -RootNavigator (Stack) -├── Splash → SplashScreen (auto-navigate vers Main après 2.8s) +RootNavigator (NativeStack) +├── Splash → SplashScreen (auto → Main apres 2.8s) ├── Main → BottomTabNavigator │ ├── Home → HomeScreen -│ ├── Scanner → ScannerScreen (bouton FAB central) -│ ├── History → HistoryScreen -│ └── Profile → ProfileScreen -└── Result (modal) → ResultScreen (slide_from_bottom) +│ ├── Guides → GuidesScreen (tabs: Maladies / Guides Pratiques) +│ ├── Scanner → ScannerScreen (FAB central vert sureleve) +│ ├── Library → LibraryScreen (grille plantes scannees) +│ └── Map → MapScreen (placeholder) +├── Result (modal) → ResultScreen (slide_from_bottom) +├── Notifications → NotificationsScreen (slide_from_right) +├── Profile → ProfileScreen (slide_from_right) +├── Settings → SettingsScreen (slide_from_right) +├── Guides → GuidesScreen (aussi accessible via stack) +└── Library → LibraryScreen (aussi accessible via stack) ``` ---- - -## Design Tokens (colors.ts) - -| Token | Hex | Usage | -|-------|-----|-------| -| `primary[700]` | `#2D6A4F` | Tab active, CTA principal | -| `primary[800]` | `#1B4332` | Scanner FAB | -| `primary[900]` | `#0A2318` | Ombres | -| `accent[500]` | `#7C3AED` | Badges, accents violet raisin | -| `surface` | `#FFFFFF` | Fond tab bar, cards | -| `background` | `#F8FBF9` | Fond écrans | -| `neutral[300]` | `#D1D5DB` | Bordures | -| `neutral[400]` | `#9CA3AF` | Tab inactive | +**Bottom Tab Bar** : Home | Guides | Scanner (FAB) | Library | Map +- Icones : lucide-react-native (House, BookOpen, ScanLine, Leaf, Map) +- FAB Scanner : cercle vert primary[800], 56px, sureleve -28px +- Haptic feedback sur chaque onglet --- -## Gamification +## Ecrans -- **7 niveaux** : Bourgeon → Apprenti Vigneron → Vigneron → Expert Viticole → Sommelier → Grand Cru → Maître de Chai -- **XP** : +10 (vigne détectée), +5 (incertain), +15 (streak bonus) -- **7 badges** : premier_scan, amateur, expert, streaker_3, streaker_7, collectionneur, marathonien -- **Streak** : scan quotidien consécutif +| Ecran | Fichier | Description | +|-------|---------|-------------| +| Home | `screens/HomeScreen.tsx` | Header VinEye + search + CTA scan + maladies carousel + alerte saison + guides | +| Guides | `screens/GuidesScreen.tsx` | Segmented control (Maladies/Guides) + listes de cartes | +| Scanner | `screens/ScannerScreen.tsx` | Camera + detection IA | +| Library | `screens/LibraryScreen.tsx` | Grille 2 colonnes plantes scannees + favoris | +| Map | `screens/MapScreen.tsx` | Placeholder — a implementer | +| Result | `screens/ResultScreen.tsx` | Resultat scan + cepage + XP | +| Notifications | `screens/NotificationsScreen.tsx` | 3 types (alerte/conseil/systeme) + mock data | +| Profile | `screens/ProfileScreen.tsx` | Hero header vert + avatar + info card + stats Bento | +| Settings | `screens/SettingsScreen.tsx` | Menus groupes + referral card orange + reset | +| History | `screens/HistoryScreen.tsx` | Legacy — remplace par Notifications | +| Splash | `screens/SplashScreen.tsx` | Animation de demarrage | --- -## Fonctionnalités clés +## Composants Home -| Feature | Fichier principal | Statut | -|---------|-------------------|--------| -| Splash animée | `screens/SplashScreen.tsx` | ✅ | -| Scanner caméra | `screens/ScannerScreen.tsx` | ✅ | -| Résultat + cépage | `screens/ResultScreen.tsx` | ✅ | -| Historique + search | `screens/HistoryScreen.tsx` | ✅ | -| Profil + badges | `screens/ProfileScreen.tsx` | ✅ | -| Gamification XP | `hooks/useGameProgress.ts` | ✅ | -| Persistance | `services/storage.ts` | ✅ | -| Bilingue FR/EN | `i18n/` | ✅ | +| Composant | Fichier | Role | +|-----------|---------|------| +| SearchHeader | `components/home/SearchHeader.tsx` | Branding VinEye + greeting + boutons notifs/profil | +| SearchSection | `components/home/SearchSection.tsx` | Barre de recherche rounded-full avec filtre | +| HomeCta | `components/home/HomeCta.tsx` | Banner scan avec animation pulse + CTA | +| FrequentDiseases | `components/home/FrequentDiseases.tsx` | Carousel horizontal maladies (160px cards) | +| SeasonAlert | `components/home/SeasonAlert.tsx` | Carte alerte saisonniere (fond vert lime) | +| PracticalGuides | `components/home/PracticalGuides.tsx` | Liste verticale guides avec chevron | +| SectionHeader | `components/home/components/homeheader.tsx` | Titre section + bouton "Voir tout" | + +--- + +## Donnees (Mock) + +| Fichier | Contenu | +|---------|---------| +| `data/diseases.ts` | 7 maladies : mildiou, oidium, black rot, esca, botrytis, flavescence doree, chlorose | +| `data/guides.ts` | 3 guides : feuille saine, calendrier traitement, cepages bordelais | + +--- + +## Design System + +- **Fond** : `#F8F9FB` (gris bleuté) +- **Cards** : `#FFFFFF`, borderRadius 24-32, border 1px `#F0F0F0` +- **Ombres** : shadowOpacity 0.04, shadowRadius 8-10 (iOS), elevation 2-3 (Android) +- **Typographie** : Regular (400) par defaut, Medium (500) titres menus, Bold (700) noms utilisateur uniquement +- **Couleurs texte** : `#1A1A1A` (titres), `#8E8E93` (sous-titres/labels) +- **Style** : Bento Box minimaliste, espaces, zen --- ## Conventions +- **Styling** : NativeWind (className) prioritaire, StyleSheet pour ombres/gradients/arrondis specifiques - Package manager : **pnpm** - Path alias : `@/*` → `src/*` -- `useEffect` toujours depuis `react` (jamais depuis `react-native-reanimated`) -- Navigation : React Navigation v7 uniquement, **jamais Expo Router** (`src/app/` est interdit — renommé en `src/screens/`) +- `useEffect` depuis `react` (jamais depuis reanimated) +- Navigation : React Navigation v7, **jamais Expo Router** - Max 300 lignes par fichier +- i18n : tous les textes via `t()`, cles dans fr.json et en.json --- ## Commandes ```bash -pnpm start # Lance Metro bundler +pnpm start # Metro bundler +pnpm web # Version web pnpm android # Build Android pnpm ios # Build iOS ``` + +--- + +## Changelog + +### 2026-04-02 — Refonte navigation + nouveaux ecrans + +#### Added +- Bottom tab bar classique avec FAB central (Home | Guides | Scanner FAB | Library | Map) +- Icones lucide-react-native pour la bottom bar +- SearchHeader : branding VinEye + greeting + boutons notifs/profil +- SearchSection : barre de recherche rounded-full avec filtre +- HomeCta : banner scan anime avec pulse reanimated +- FrequentDiseases : carousel horizontal 7 maladies (cards Bento 160px) +- SeasonAlert : carte alerte saisonniere +- PracticalGuides : liste verticale 3 guides +- NotificationsScreen : 3 types (alerte/conseil/systeme), 6 mock, mark all read, empty state +- ProfileScreen : hero header vert + avatar overlap + info card + stats Bento 2x2 +- SettingsScreen : menus groupes + referral card orange + language toggle + reset +- GuidesScreen : segmented control (Maladies/Guides) + listes de cartes avec badges severite +- LibraryScreen : grille 2 colonnes plantes + toggle favoris coeur +- MapScreen : placeholder +- data/diseases.ts : 7 maladies de la vigne typees +- data/guides.ts : 3 guides pratiques types +- Traductions completes FR/EN pour tous les nouveaux ecrans + +#### Changed +- Navigation restructuree : History/Profile retires du tab bar → accessibles via header +- HomeScreen simplifie : header + search + CTA + 3 sections contenu +- react-dom aligne sur react 19.1.0 + +#### Removed +- Ancien floating pill tab bar (LayoutAnimation buggue) +- StatisticsSection du HomeScreen (deplace vers ProfileScreen) + +--- + +**Version** : 2.0.0 +**Derniere mise a jour** : 2026-04-02 diff --git a/VinEye/src/components/home/FrequentDiseases.tsx b/VinEye/src/components/home/FrequentDiseases.tsx new file mode 100644 index 0000000..822b408 --- /dev/null +++ b/VinEye/src/components/home/FrequentDiseases.tsx @@ -0,0 +1,149 @@ +import { View, FlatList, TouchableOpacity, StyleSheet, Platform } from "react-native"; +import { useTranslation } from "react-i18next"; +import { Ionicons } from "@expo/vector-icons"; + +import { Text } from "@/components/ui/text"; +import { colors } from "@/theme/colors"; +import { VINE_DISEASES } from "@/data/diseases"; +import type { Disease } from "@/data/diseases"; + +const DISEASE_TYPE_KEYS: Record = { + fungal: "diseases.types.fungal", + bacterial: "diseases.types.bacterial", + pest: "diseases.types.pest", + abiotic: "diseases.types.abiotic", +}; + +const SEVERITY_LEVELS: Record = { + high: { color: "#EF4444", label: "high" }, + medium: { color: "#F59E0B", label: "medium" }, + low: { color: "#10B981", label: "low" }, +}; + +export default function FrequentDiseases() { + const { t } = useTranslation(); + + return ( + item.id} + horizontal + showsHorizontalScrollIndicator={false} + contentContainerStyle={styles.listContainer} + renderItem={({ item }) => { + const severity = SEVERITY_LEVELS[item.severity]; + + return ( + + {/* Header: Icon & Severity Badge */} + + + + + + + + + + + {/* Content */} + + + {t(DISEASE_TYPE_KEYS[item.type]).toUpperCase()} + + + {t(item.name)} + + + + {/* Footer: Action hint */} + + {t("common.details")} + + + + ); + }} + /> + ); +} + +const styles = StyleSheet.create({ + listContainer: { + paddingHorizontal: 20, + paddingVertical: 10, + gap: 16, + }, + card: { + width: 160, + backgroundColor: "#FFFFFF", + borderRadius: 24, + padding: 16, + justifyContent: "space-between", + // Shadow logic + ...Platform.select({ + ios: { + shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.12, + shadowRadius: 12, + }, + android: { + elevation: 6, + }, + }), + }, + cardHeader: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "flex-start", + marginBottom: 12, + }, + iconWrapper: { + width: 44, + height: 44, + borderRadius: 14, + alignItems: "center", + justifyContent: "center", + }, + severityBadge: { + padding: 6, + borderRadius: 10, + alignItems: "center", + justifyContent: "center", + }, + dot: { + width: 8, + height: 8, + borderRadius: 4, + }, + cardBody: { + flex: 1, + }, + typeText: { + fontSize: 10, + fontWeight: "800", + color: colors.neutral[400], + letterSpacing: 0.5, + marginBottom: 4, + }, + nameText: { + fontSize: 15, + fontWeight: "700", + color: colors.neutral[900], + lineHeight: 20, + }, + cardFooter: { + flexDirection: "row", + alignItems: "center", + marginTop: 12, + gap: 4, + }, + moreInfo: { + fontSize: 12, + fontWeight: "600", + color: colors.neutral[400], + } +}); \ No newline at end of file diff --git a/VinEye/src/components/home/HomeCta.tsx b/VinEye/src/components/home/HomeCta.tsx new file mode 100644 index 0000000..9be99a7 --- /dev/null +++ b/VinEye/src/components/home/HomeCta.tsx @@ -0,0 +1,191 @@ +import { useEffect } from "react"; +import { View, TouchableOpacity, StyleSheet, Platform } from "react-native"; +import { useTranslation } from "react-i18next"; +import { useNavigation } from "@react-navigation/native"; +import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; +import { Ionicons, MaterialIcons } from "@expo/vector-icons"; +import Animated, { + useSharedValue, + useAnimatedStyle, + withRepeat, + withSequence, + withTiming, +} from "react-native-reanimated"; + +import { Text } from "@/components/ui/text"; +import { colors } from "@/theme/colors"; +import type { RootStackParamList } from "@/types/navigation"; + +type Nav = NativeStackNavigationProp; + +export default function HeroScanner() { + const { t } = useTranslation(); + const navigation = useNavigation