chore(dev): mock seed for testing the Map without scanning

Adds a "Add mock plants" entry under a new Developer section in the
Settings screen, gated by __DEV__ so it never ships in release. It
calls useHistory.seedTestData() which prepends 5 fake ScanRecords
spread across Bordeaux / Bourgogne / Champagne so all the map
features (region chips, markers, rename) can be exercised without
having to actually walk into a vineyard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yanis 2026-04-30 12:32:25 +02:00
parent d30f4f250c
commit 06be3483d7
2 changed files with 149 additions and 1 deletions

124
VinEye/src/data/mockSeed.ts Normal file
View file

@ -0,0 +1,124 @@
import type { ScanRecord } from '@/types/detection';
function generateId(suffix: string): string {
return `seed-${Date.now()}-${suffix}`;
}
function isoMinusDays(days: number): string {
return new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
}
export function buildMockScans(): ScanRecord[] {
return [
{
id: generateId('1'),
createdAt: isoMinusDays(0),
xpEarned: 25,
latitude: 44.84,
longitude: -0.58,
locationCapturedAt: isoMinusDays(0),
customName: 'Vigne du potager',
detection: {
result: 'vine',
confidence: 0.94,
diseaseClass: 'healthy',
cepageId: 'cabernet_sauvignon',
timestamp: Date.now(),
allProbabilities: [
{ class: 'healthy', probability: 0.94 },
{ class: 'esca', probability: 0.03 },
{ class: 'black_rot', probability: 0.02 },
{ class: 'leaf_blight', probability: 0.01 },
],
},
},
{
id: generateId('2'),
createdAt: isoMinusDays(1),
xpEarned: 35,
latitude: 44.66,
longitude: -0.35,
locationCapturedAt: isoMinusDays(1),
detection: {
result: 'vine',
confidence: 0.81,
diseaseClass: 'esca',
diseaseSlug: 'esca',
cepageId: 'merlot',
timestamp: Date.now() - 86_400_000,
allProbabilities: [
{ class: 'esca', probability: 0.81 },
{ class: 'healthy', probability: 0.12 },
{ class: 'leaf_blight', probability: 0.05 },
{ class: 'black_rot', probability: 0.02 },
],
},
},
{
id: generateId('3'),
createdAt: isoMinusDays(2),
xpEarned: 30,
latitude: 47.32,
longitude: 4.83,
locationCapturedAt: isoMinusDays(2),
customName: 'Pinot du grand-père',
detection: {
result: 'vine',
confidence: 0.88,
diseaseClass: 'healthy',
cepageId: 'pinot_noir',
timestamp: Date.now() - 2 * 86_400_000,
allProbabilities: [
{ class: 'healthy', probability: 0.88 },
{ class: 'leaf_blight', probability: 0.07 },
{ class: 'esca', probability: 0.03 },
{ class: 'black_rot', probability: 0.02 },
],
},
},
{
id: generateId('4'),
createdAt: isoMinusDays(3),
xpEarned: 20,
latitude: 49.05,
longitude: 4.05,
locationCapturedAt: isoMinusDays(3),
detection: {
result: 'uncertain',
confidence: 0.55,
diseaseClass: 'leaf_blight',
diseaseSlug: 'leaf-blight',
cepageId: 'chardonnay',
timestamp: Date.now() - 3 * 86_400_000,
allProbabilities: [
{ class: 'leaf_blight', probability: 0.55 },
{ class: 'healthy', probability: 0.28 },
{ class: 'esca', probability: 0.10 },
{ class: 'black_rot', probability: 0.07 },
],
},
},
{
id: generateId('5'),
createdAt: isoMinusDays(4),
xpEarned: 35,
latitude: 44.92,
longitude: -0.62,
locationCapturedAt: isoMinusDays(4),
detection: {
result: 'vine',
confidence: 0.76,
diseaseClass: 'black_rot',
diseaseSlug: 'black-rot',
cepageId: 'cabernet_sauvignon',
timestamp: Date.now() - 4 * 86_400_000,
allProbabilities: [
{ class: 'black_rot', probability: 0.76 },
{ class: 'healthy', probability: 0.15 },
{ class: 'esca', probability: 0.06 },
{ class: 'leaf_blight', probability: 0.03 },
],
},
},
];
}

View file

@ -12,6 +12,8 @@ import { useTranslation } from "react-i18next";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import i18n from "@/i18n"; import i18n from "@/i18n";
import { toast } from "sonner-native";
import { Text } from "@/components/ui/text"; import { Text } from "@/components/ui/text";
import { colors } from "@/theme/colors"; import { colors } from "@/theme/colors";
import { useGameProgress } from "@/hooks/useGameProgress"; import { useGameProgress } from "@/hooks/useGameProgress";
@ -30,7 +32,12 @@ export default function SettingsScreen() {
const { t } = useTranslation(); const { t } = useTranslation();
const navigation = useNavigation(); const navigation = useNavigation();
const { resetProgress } = useGameProgress(); const { resetProgress } = useGameProgress();
const { clearHistory } = useHistory(); const { clearHistory, seedTestData } = useHistory();
async function handleSeed() {
await seedTestData();
toast.success(t("settings.seedDone"));
}
function handleLanguageToggle() { function handleLanguageToggle() {
const newLang = i18n.language === "fr" ? "en" : "fr"; const newLang = i18n.language === "fr" ? "en" : "fr";
@ -93,6 +100,16 @@ export default function SettingsScreen() {
}, },
]; ];
const devItems: MenuItem[] = __DEV__
? [
{
icon: "flask-outline",
label: t("settings.seedTestData"),
onPress: handleSeed,
},
]
: [];
const dangerItems: MenuItem[] = [ const dangerItems: MenuItem[] = [
{ {
icon: "trash-outline", icon: "trash-outline",
@ -187,6 +204,13 @@ export default function SettingsScreen() {
</View> </View>
</TouchableOpacity> </TouchableOpacity>
{devItems.length > 0 && (
<>
<Text style={styles.sectionLabel}>{t("settings.developer")}</Text>
{renderMenuGroup(devItems)}
</>
)}
{renderMenuGroup(dangerItems)} {renderMenuGroup(dangerItems)}
<Text style={styles.versionText}>VinEye Version 1.0.0</Text> <Text style={styles.versionText}>VinEye Version 1.0.0</Text>