App copy
This commit is contained in:
@@ -37,35 +37,56 @@ interface Message {
|
||||
const getChatCopy = (language: Language) => {
|
||||
if (language === 'de') {
|
||||
return {
|
||||
title: 'Monstera',
|
||||
title: 'GreenLens',
|
||||
skip: 'Überspringen',
|
||||
continue: 'Weiter',
|
||||
today: 'Heute, 20:42 Uhr',
|
||||
typing: 'schreibt...',
|
||||
botGreeting: "Hi! Ich bin deine neue Monstera. 🌿 Ich freue mich riesig darauf, mit dir zu wachsen!",
|
||||
botSource: "Als erstes: Wie hast du von GreenLens gehört?",
|
||||
botGoal: "Verstanden! Und was ist dein Hauptziel mit deinen Pflanzen?",
|
||||
botExperience: "Schön! Wie erfahren bist du in der Pflanzenpflege?",
|
||||
botLight: "Damit ich glücklich bleibe und diese großen, schönen Blätter wachsen, muss ich ein bisschen über mein neues Zuhause wissen. Wie viel Licht bekomme ich hier?",
|
||||
botComplete: "Perfekt! Ich bin bereit einzuziehen. Lass uns deinen Pflegeplan erstellen!",
|
||||
// Reihenfolge folgt dem Nutzer, nicht dem Unternehmen: erst sein Problem,
|
||||
// dann Kontext, zuletzt die Marketing-Frage.
|
||||
botGreeting: 'Hi! Ich helfe dir herauszufinden, was mit deinen Pflanzen los ist. 🌿',
|
||||
botSituation: 'Damit ich weiß, wo wir starten: Hat gerade eine deiner Pflanzen ein Problem?',
|
||||
botSymptom: 'Verstanden. Was siehst du genau? Das grenzt die möglichen Ursachen am stärksten ein.',
|
||||
botNoSymptom: 'Gut zu wissen - dann richten wir alles auf Vorbeugung aus.',
|
||||
botLight: 'Licht entscheidet bei den meisten Problemen mit. Wie viel Licht bekommt deine Pflanze?',
|
||||
botExperience: 'Fast geschafft. Wie sicher fühlst du dich bei Pflanzenpflege? Danach passe ich an, wie ausführlich ich erkläre.',
|
||||
botSource: 'Letzte Frage, rein aus Neugier: Wie hast du von GreenLens gehört?',
|
||||
botComplete: 'Perfekt. Ich stelle dir jetzt deinen Plan zusammen.',
|
||||
stepSituation: 'Hat eine Pflanze gerade ein Problem?',
|
||||
stepSymptom: 'Was siehst du?',
|
||||
stepLight: 'Wie viel Licht bekommt sie?',
|
||||
stepExperience: 'Wie sicher fühlst du dich?',
|
||||
stepSource: 'Wie hast du von uns gehört?',
|
||||
situations: {
|
||||
acute: 'Ja - und es wird schnell schlechter',
|
||||
slow: 'Ja - es verändert sich langsam',
|
||||
prevention: 'Nein - ich will vorbeugen',
|
||||
},
|
||||
symptoms: {
|
||||
yellow: 'Gelbe Blätter',
|
||||
brown_tips: 'Braune Spitzen oder Ränder',
|
||||
drooping: 'Hängt oder lässt die Blätter fallen',
|
||||
spots: 'Flecken oder Belag',
|
||||
pests: 'Krabbeltiere',
|
||||
other: 'Etwas anderes',
|
||||
},
|
||||
sources: {
|
||||
app_store: 'App Store / Play Store',
|
||||
instagram: 'Instagram',
|
||||
tiktok: 'TikTok',
|
||||
friend: 'Freunde oder Familie',
|
||||
search: 'Google oder Suche',
|
||||
// Bewusst unwahrscheinliche Optionen: sie brechen das Muster der
|
||||
// Standard-Attributionsfrage und machen den letzten Schritt leichter.
|
||||
tv: 'Im Fernsehen (haben wir nicht - aber schön wärs)',
|
||||
radio: 'Im Radio zwischen zwei Songs',
|
||||
plant: 'Meine Pflanze hat es mir empfohlen',
|
||||
other: 'Etwas anderes',
|
||||
},
|
||||
goals: {
|
||||
identify: 'Pflanzen erkennen',
|
||||
care: 'Pflegepläne',
|
||||
collection: 'Sammlung vergrößern',
|
||||
learn: 'Über Pflanzen lernen',
|
||||
},
|
||||
experiences: {
|
||||
beginner: 'Anfänger (Ich bin neu)',
|
||||
intermediate: 'Fortgeschritten (Ich habe Pflanzen)',
|
||||
advanced: 'Experte (Ich weiß Bescheid)',
|
||||
beginner: 'Unsicher - ich fange gerade an',
|
||||
intermediate: 'Geht so - ich habe ein paar Pflanzen',
|
||||
advanced: 'Sicher - ich kenne mich aus',
|
||||
},
|
||||
lights: {
|
||||
bright_indirect: {
|
||||
@@ -85,35 +106,52 @@ const getChatCopy = (language: Language) => {
|
||||
}
|
||||
if (language === 'es') {
|
||||
return {
|
||||
title: 'Monstera',
|
||||
title: 'GreenLens',
|
||||
skip: 'Omitir',
|
||||
continue: 'Continuar',
|
||||
today: 'Hoy, 8:42 PM',
|
||||
typing: 'escribiendo...',
|
||||
botGreeting: "¡Hola! Soy tu nueva Monstera. 🌿 ¡Estoy muy emocionada de crecer contigo!",
|
||||
botSource: "Primero, ¿cómo te enteraste de GreenLens?",
|
||||
botGoal: "¡Entendido! ¿Y cuál es tu objetivo principal con tus plantas?",
|
||||
botExperience: "¡Genial! ¿Qué experiencia tienes en el cuidado de plantas?",
|
||||
botLight: "Para estar feliz y que crezcan esas hojas grandes y hermosas, necesito saber un poco sobre mi nuevo hogar. ¿Cuánta luz recibiré aquí?",
|
||||
botComplete: "¡Perfecto! Estoy lista para mudarme. ¡Creemos tu plan de cuidados!",
|
||||
botGreeting: '¡Hola! Te ayudo a averiguar qué les pasa a tus plantas. 🌿',
|
||||
botSituation: 'Para saber por dónde empezamos: ¿alguna de tus plantas tiene un problema ahora mismo?',
|
||||
botSymptom: 'Entendido. ¿Qué ves exactamente? Esto es lo que más acota las causas posibles.',
|
||||
botNoSymptom: 'Bien saberlo - entonces lo enfocamos todo en la prevención.',
|
||||
botLight: 'La luz influye en la mayoría de los problemas. ¿Cuánta luz recibe tu planta?',
|
||||
botExperience: 'Casi listo. ¿Qué tan seguro te sientes cuidando plantas? Con eso ajusto cuánto explico.',
|
||||
botSource: 'Última pregunta, por curiosidad: ¿cómo te enteraste de GreenLens?',
|
||||
botComplete: 'Perfecto. Ahora preparo tu plan.',
|
||||
stepSituation: '¿Alguna planta tiene un problema?',
|
||||
stepSymptom: '¿Qué ves?',
|
||||
stepLight: '¿Cuánta luz recibe?',
|
||||
stepExperience: '¿Qué tan seguro te sientes?',
|
||||
stepSource: '¿Cómo nos conociste?',
|
||||
situations: {
|
||||
acute: 'Sí - y empeora rápido',
|
||||
slow: 'Sí - cambia poco a poco',
|
||||
prevention: 'No - quiero prevenir',
|
||||
},
|
||||
symptoms: {
|
||||
yellow: 'Hojas amarillas',
|
||||
brown_tips: 'Puntas o bordes marrones',
|
||||
drooping: 'Se cae o pierde hojas',
|
||||
spots: 'Manchas o residuo',
|
||||
pests: 'Bichos',
|
||||
other: 'Otra cosa',
|
||||
},
|
||||
sources: {
|
||||
app_store: 'App Store / Play Store',
|
||||
instagram: 'Instagram',
|
||||
tiktok: 'TikTok',
|
||||
friend: 'Amigos o familia',
|
||||
search: 'Google o búsqueda',
|
||||
tv: 'En la tele (no tenemos anuncios - pero ojalá)',
|
||||
radio: 'En la radio entre dos canciones',
|
||||
plant: 'Me la recomendó mi planta',
|
||||
other: 'Otra cosa',
|
||||
},
|
||||
goals: {
|
||||
identify: 'Identificar plantas',
|
||||
care: 'Horarios de cuidado',
|
||||
collection: 'Crecer mi colección',
|
||||
learn: 'Aprender sobre plantas',
|
||||
},
|
||||
experiences: {
|
||||
beginner: 'Principiante (Soy nuevo)',
|
||||
intermediate: 'Intermedio (Tengo plantas)',
|
||||
advanced: 'Experto (Sé bastante)',
|
||||
beginner: 'Inseguro - recién empiezo',
|
||||
intermediate: 'Más o menos - tengo algunas plantas',
|
||||
advanced: 'Seguro - sé bastante',
|
||||
},
|
||||
lights: {
|
||||
bright_indirect: {
|
||||
@@ -132,35 +170,52 @@ const getChatCopy = (language: Language) => {
|
||||
};
|
||||
}
|
||||
return {
|
||||
title: 'Monstera',
|
||||
title: 'GreenLens',
|
||||
skip: 'Skip',
|
||||
continue: 'Continue',
|
||||
today: 'Today, 8:42 PM',
|
||||
typing: 'typing...',
|
||||
botGreeting: "Hi! I'm your new Monstera. 🌿 I'm so excited to grow with you!",
|
||||
botSource: "First, how did you hear about GreenLens?",
|
||||
botGoal: "Got it! And what is your main goal with your plants?",
|
||||
botExperience: "Nice! How experienced are you with plant care?",
|
||||
botLight: "To stay happy and grow those big, beautiful leaves, I need to know a bit about my new home. How much light do I get here?",
|
||||
botComplete: "Perfect! I'm ready to move in. Let's build your care plan!",
|
||||
botGreeting: "Hi! I'll help you work out what's going on with your plants. 🌿",
|
||||
botSituation: 'So I know where to start: is one of your plants having a problem right now?',
|
||||
botSymptom: 'Got it. What exactly do you see? This narrows down the possible causes more than anything else.',
|
||||
botNoSymptom: "Good to know - then we'll focus everything on prevention.",
|
||||
botLight: 'Light plays a role in most problems. How much light does your plant get?',
|
||||
botExperience: 'Almost done. How confident do you feel about plant care? I use that to adjust how much I explain.',
|
||||
botSource: 'Last question, purely out of curiosity: how did you hear about GreenLens?',
|
||||
botComplete: "Perfect. I'm putting your plan together now.",
|
||||
stepSituation: 'Is a plant having a problem?',
|
||||
stepSymptom: 'What do you see?',
|
||||
stepLight: 'How much light does it get?',
|
||||
stepExperience: 'How confident do you feel?',
|
||||
stepSource: 'How did you hear about us?',
|
||||
situations: {
|
||||
acute: "Yes - and it's getting worse fast",
|
||||
slow: "Yes - it's changing slowly",
|
||||
prevention: 'No - I want to prevent problems',
|
||||
},
|
||||
symptoms: {
|
||||
yellow: 'Yellow leaves',
|
||||
brown_tips: 'Brown tips or edges',
|
||||
drooping: 'Drooping or dropping leaves',
|
||||
spots: 'Spots or residue',
|
||||
pests: 'Bugs',
|
||||
other: 'Something else',
|
||||
},
|
||||
sources: {
|
||||
app_store: 'App Store / Play Store',
|
||||
instagram: 'Instagram',
|
||||
tiktok: 'TikTok',
|
||||
friend: 'Friends or family',
|
||||
search: 'Google or search',
|
||||
tv: "On TV (we don't run ads - but a girl can dream)",
|
||||
radio: 'On the radio between two songs',
|
||||
plant: 'My plant recommended it',
|
||||
other: 'Something else',
|
||||
},
|
||||
goals: {
|
||||
identify: 'Identify plants',
|
||||
care: 'Care schedules',
|
||||
collection: 'Grow collection',
|
||||
learn: 'Learn about plants',
|
||||
},
|
||||
experiences: {
|
||||
beginner: 'Beginner (I\'m a new plant parent)',
|
||||
intermediate: 'Intermediate (I have some plants)',
|
||||
advanced: 'Advanced (I\'m a plant expert)',
|
||||
beginner: "Not confident - I'm just starting",
|
||||
intermediate: 'So-so - I have a few plants',
|
||||
advanced: 'Confident - I know my way around',
|
||||
},
|
||||
lights: {
|
||||
bright_indirect: {
|
||||
@@ -179,6 +234,11 @@ const getChatCopy = (language: Language) => {
|
||||
};
|
||||
};
|
||||
|
||||
/** Fragenreihenfolge des Chats. Bewusst: Problem zuerst, Attribution zuletzt. */
|
||||
const STEP_KEYS = ['situation', 'symptom', 'lightLevel', 'experienceLevel', 'acquisitionSource'] as const;
|
||||
type StepKey = (typeof STEP_KEYS)[number];
|
||||
const TOTAL_STEPS = STEP_KEYS.length;
|
||||
|
||||
const TypingIndicator = ({ colors }: { colors: any }) => {
|
||||
const dot1 = useRef(new Animated.Value(0)).current;
|
||||
const dot2 = useRef(new Animated.Value(0)).current;
|
||||
@@ -233,7 +293,7 @@ export default function OnboardingChatScreen() {
|
||||
const posthog = useSafeAnalytics();
|
||||
const { session, colorPalette, language } = useApp();
|
||||
// This screen is hardcoded dark (#131e14 backgrounds), so always use the
|
||||
// dark color set — theme-dependent text would be unreadable in light mode.
|
||||
// dark color set - theme-dependent text would be unreadable in light mode.
|
||||
const colors = useColors(true, colorPalette);
|
||||
const copy = getChatCopy(language);
|
||||
const { height } = useWindowDimensions();
|
||||
@@ -244,10 +304,11 @@ export default function OnboardingChatScreen() {
|
||||
const [isTyping, setIsTyping] = useState<boolean>(false);
|
||||
const [showOptions, setShowOptions] = useState<boolean>(false);
|
||||
const [answers, setAnswers] = useState({
|
||||
acquisitionSource: null as string | null,
|
||||
primaryGoal: null as string | null,
|
||||
experienceLevel: null as string | null,
|
||||
situation: null as string | null,
|
||||
symptom: null as string | null,
|
||||
lightLevel: null as string | null,
|
||||
experienceLevel: null as string | null,
|
||||
acquisitionSource: null as string | null,
|
||||
});
|
||||
|
||||
const flatListRef = useRef<FlatList>(null);
|
||||
@@ -296,9 +357,9 @@ export default function OnboardingChatScreen() {
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: 'question-source',
|
||||
id: 'question-situation',
|
||||
sender: 'bot',
|
||||
text: copy.botSource,
|
||||
text: copy.botSituation,
|
||||
timestamp: copy.today,
|
||||
},
|
||||
]);
|
||||
@@ -331,15 +392,17 @@ export default function OnboardingChatScreen() {
|
||||
};
|
||||
setMessages((prev) => [...prev, userMsg]);
|
||||
|
||||
const nextStep = currentStep + 1;
|
||||
// Wer "vorbeugen" waehlt, hat kein Symptom - die Symptomfrage wird
|
||||
// uebersprungen. Ohne das antwortet der Bot "dann beugen wir vor" und
|
||||
// fragt im selben Atemzug "Was siehst du?".
|
||||
const skipsSymptom = currentStep === 0 && id === 'prevention';
|
||||
const nextStep = skipsSymptom ? 2 : currentStep + 1;
|
||||
setCurrentStep(nextStep);
|
||||
|
||||
// Save answer
|
||||
const nextAnswers = { ...answers };
|
||||
if (currentStep === 0) nextAnswers.acquisitionSource = id;
|
||||
else if (currentStep === 1) nextAnswers.primaryGoal = id;
|
||||
else if (currentStep === 2) nextAnswers.experienceLevel = id;
|
||||
else if (currentStep === 3) nextAnswers.lightLevel = id;
|
||||
const stepKey: StepKey | undefined = STEP_KEYS[currentStep];
|
||||
if (stepKey) nextAnswers[stepKey] = id;
|
||||
setAnswers(nextAnswers);
|
||||
|
||||
// Trigger bot response typing
|
||||
@@ -350,12 +413,16 @@ export default function OnboardingChatScreen() {
|
||||
let botSubtext = undefined;
|
||||
|
||||
if (nextStep === 1) {
|
||||
botText = copy.botGoal;
|
||||
botText = copy.botSymptom;
|
||||
} else if (nextStep === 2) {
|
||||
botText = copy.botExperience;
|
||||
// Nach dem uebersprungenen Symptomschritt zuerst die Ueberleitung,
|
||||
// damit die Antwort des Nutzers nicht unkommentiert bleibt.
|
||||
botText = skipsSymptom ? `${copy.botNoSymptom} ${copy.botLight}` : copy.botLight;
|
||||
} else if (nextStep === 3) {
|
||||
botText = copy.botLight;
|
||||
botText = copy.botExperience;
|
||||
} else if (nextStep === 4) {
|
||||
botText = copy.botSource;
|
||||
} else if (nextStep === TOTAL_STEPS) {
|
||||
botText = copy.botComplete;
|
||||
}
|
||||
|
||||
@@ -382,15 +449,17 @@ export default function OnboardingChatScreen() {
|
||||
return;
|
||||
}
|
||||
|
||||
const prevStep = currentStep - 1;
|
||||
// Gegenstueck zum uebersprungenen Symptomschritt: von Schritt 2 zurueck
|
||||
// landet ein Vorbeuger wieder bei der Situationsfrage, nicht bei einer
|
||||
// Symptomfrage, die er nie gesehen hat.
|
||||
const skippedSymptom = currentStep === 2 && answers.situation === 'prevention';
|
||||
const prevStep = skippedSymptom ? 0 : currentStep - 1;
|
||||
setCurrentStep(prevStep);
|
||||
|
||||
// Revert last answers
|
||||
const nextAnswers = { ...answers };
|
||||
if (prevStep === 0) nextAnswers.acquisitionSource = null;
|
||||
else if (prevStep === 1) nextAnswers.primaryGoal = null;
|
||||
else if (prevStep === 2) nextAnswers.experienceLevel = null;
|
||||
else if (prevStep === 3) nextAnswers.lightLevel = null;
|
||||
const prevKey: StepKey | undefined = STEP_KEYS[prevStep];
|
||||
if (prevKey) nextAnswers[prevKey] = null;
|
||||
setAnswers(nextAnswers);
|
||||
|
||||
// Remove user bubble and bot bubble from messages history
|
||||
@@ -411,20 +480,22 @@ export default function OnboardingChatScreen() {
|
||||
const onFinish = () => {
|
||||
if (session?.userId) {
|
||||
if (answers.acquisitionSource) OnboardingProgressService.setAcquisitionSource(session.userId, answers.acquisitionSource);
|
||||
if (answers.primaryGoal) OnboardingProgressService.setPrimaryGoal(session.userId, answers.primaryGoal);
|
||||
if (answers.experienceLevel) OnboardingProgressService.setExperienceLevel(session.userId, answers.experienceLevel);
|
||||
}
|
||||
|
||||
|
||||
if (answers.acquisitionSource) void PreAuthOnboardingService.setAnswer('acquisitionSource', answers.acquisitionSource);
|
||||
if (answers.primaryGoal) void PreAuthOnboardingService.setAnswer('primaryGoal', answers.primaryGoal);
|
||||
if (answers.experienceLevel) void PreAuthOnboardingService.setAnswer('experienceLevel', answers.experienceLevel);
|
||||
if (answers.lightLevel) void PreAuthOnboardingService.setAnswer('lightLevel', answers.lightLevel);
|
||||
// Situation und Symptom steuern spaeter die Personalisierung des Plans.
|
||||
if (answers.situation) void PreAuthOnboardingService.setAnswer('situation', answers.situation);
|
||||
if (answers.symptom) void PreAuthOnboardingService.setAnswer('symptom', answers.symptom);
|
||||
|
||||
posthog.capture('onboarding_chat_completed', {
|
||||
source: answers.acquisitionSource,
|
||||
goal: answers.primaryGoal,
|
||||
experience: answers.experienceLevel,
|
||||
situation: answers.situation,
|
||||
symptom: answers.symptom,
|
||||
light: answers.lightLevel,
|
||||
experience: answers.experienceLevel,
|
||||
source: answers.acquisitionSource,
|
||||
});
|
||||
|
||||
router.replace('/onboarding/health-check');
|
||||
@@ -434,18 +505,43 @@ export default function OnboardingChatScreen() {
|
||||
const renderOptions = () => {
|
||||
if (!showOptions || isTyping) return null;
|
||||
|
||||
// Schritt 1 - Situation. Erste Frage gehoert dem Nutzer, nicht dem Marketing.
|
||||
if (currentStep === 0) {
|
||||
return (
|
||||
<View style={styles.optionsWrap}>
|
||||
<Text style={styles.stepTitle}>How did you find GreenLens?</Text>
|
||||
<Text style={styles.stepTitle}>{copy.stepSituation}</Text>
|
||||
<View style={styles.optionsVertical}>
|
||||
{([
|
||||
{ id: 'acute', label: copy.situations.acute, icon: 'alert-circle-outline' as const },
|
||||
{ id: 'slow', label: copy.situations.slow, icon: 'trending-down-outline' as const },
|
||||
{ id: 'prevention', label: copy.situations.prevention, icon: 'shield-checkmark-outline' as const },
|
||||
]).map((opt) => (
|
||||
<TouchableOpacity key={opt.id} style={[styles.optionRow, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
|
||||
<View style={styles.optionRowLeft}>
|
||||
<Ionicons name={opt.icon} size={18} color={colors.primary} style={{ marginRight: 10 }} />
|
||||
<Text style={[styles.rowText, { color: colors.text }]}>{opt.label}</Text>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={16} color={colors.textMuted} />
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
// Schritt 2 - Symptom. Der Moment, in dem sich der Nutzer wiedererkennt.
|
||||
if (currentStep === 1) {
|
||||
return (
|
||||
<View style={styles.optionsWrap}>
|
||||
<Text style={styles.stepTitle}>{copy.stepSymptom}</Text>
|
||||
<View style={styles.optionsGrid}>
|
||||
{([
|
||||
{ id: 'app_store', label: copy.sources.app_store, icon: 'logo-apple' as const },
|
||||
{ id: 'instagram', label: copy.sources.instagram, icon: 'logo-instagram' as const },
|
||||
{ id: 'tiktok', label: copy.sources.tiktok, icon: 'logo-youtube' as const },
|
||||
{ id: 'friend', label: copy.sources.friend, icon: 'people-outline' as const },
|
||||
{ id: 'search', label: copy.sources.search, icon: 'search-outline' as const },
|
||||
{ id: 'other', label: copy.sources.other, icon: 'sparkles-outline' as const },
|
||||
{ id: 'yellow', label: copy.symptoms.yellow, icon: 'leaf-outline' as const },
|
||||
{ id: 'brown_tips', label: copy.symptoms.brown_tips, icon: 'flame-outline' as const },
|
||||
{ id: 'drooping', label: copy.symptoms.drooping, icon: 'trending-down-outline' as const },
|
||||
{ id: 'spots', label: copy.symptoms.spots, icon: 'ellipse-outline' as const },
|
||||
{ id: 'pests', label: copy.symptoms.pests, icon: 'bug-outline' as const },
|
||||
{ id: 'other', label: copy.symptoms.other, icon: 'help-circle-outline' as const },
|
||||
]).map((opt) => (
|
||||
<TouchableOpacity key={opt.id} style={[styles.pillBtn, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
|
||||
<Ionicons name={opt.icon} size={15} color={colors.primary} style={{ marginRight: 4 }} />
|
||||
@@ -457,57 +553,11 @@ export default function OnboardingChatScreen() {
|
||||
);
|
||||
}
|
||||
|
||||
if (currentStep === 1) {
|
||||
return (
|
||||
<View style={styles.optionsWrap}>
|
||||
<Text style={styles.stepTitle}>What is your main goal?</Text>
|
||||
<View style={styles.optionsVertical}>
|
||||
{([
|
||||
{ id: 'identify', label: copy.goals.identify, icon: 'search-outline' as const },
|
||||
{ id: 'care', label: copy.goals.care, icon: 'water-outline' as const },
|
||||
{ id: 'collection', label: copy.goals.collection, icon: 'leaf-outline' as const },
|
||||
{ id: 'learn', label: copy.goals.learn, icon: 'book-outline' as const },
|
||||
]).map((opt) => (
|
||||
<TouchableOpacity key={opt.id} style={[styles.optionRow, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
|
||||
<View style={styles.optionRowLeft}>
|
||||
<Ionicons name={opt.icon} size={18} color={colors.primary} style={{ marginRight: 10 }} />
|
||||
<Text style={[styles.rowText, { color: colors.text }]}>{opt.label}</Text>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={16} color={colors.textMuted} />
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
// Schritt 3 - Licht.
|
||||
if (currentStep === 2) {
|
||||
return (
|
||||
<View style={styles.optionsWrap}>
|
||||
<Text style={styles.stepTitle}>What is your experience level?</Text>
|
||||
<View style={styles.optionsVertical}>
|
||||
{([
|
||||
{ id: 'beginner', label: copy.experiences.beginner, icon: 'leaf-outline' as const },
|
||||
{ id: 'intermediate', label: copy.experiences.intermediate, icon: 'sunny-outline' as const },
|
||||
{ id: 'advanced', label: copy.experiences.advanced, icon: 'flask-outline' as const },
|
||||
]).map((opt) => (
|
||||
<TouchableOpacity key={opt.id} style={[styles.optionRow, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
|
||||
<View style={styles.optionRowLeft}>
|
||||
<Ionicons name={opt.icon} size={18} color={colors.primary} style={{ marginRight: 10 }} />
|
||||
<Text style={[styles.rowText, { color: colors.text }]}>{opt.label}</Text>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={16} color={colors.textMuted} />
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
if (currentStep === 3) {
|
||||
return (
|
||||
<View style={styles.optionsWrap}>
|
||||
<Text style={styles.stepTitle}>How much light do I get here?</Text>
|
||||
<Text style={styles.stepTitle}>{copy.stepLight}</Text>
|
||||
<View style={styles.optionsVertical}>
|
||||
{([
|
||||
{ id: 'bright_indirect', label: copy.lights.bright_indirect.title, sub: copy.lights.bright_indirect.sub, icon: 'wb-twilight' },
|
||||
@@ -527,7 +577,62 @@ export default function OnboardingChatScreen() {
|
||||
);
|
||||
}
|
||||
|
||||
// Schritt 4 - Erfahrung. Formuliert als Sicherheitsgefuehl, nicht als
|
||||
// Wissenstest: Emma soll sich nicht schon hier als Anfaengerin abstempeln.
|
||||
if (currentStep === 3) {
|
||||
return (
|
||||
<View style={styles.optionsWrap}>
|
||||
<Text style={styles.stepTitle}>{copy.stepExperience}</Text>
|
||||
<View style={styles.optionsVertical}>
|
||||
{([
|
||||
{ id: 'beginner', label: copy.experiences.beginner, icon: 'leaf-outline' as const },
|
||||
{ id: 'intermediate', label: copy.experiences.intermediate, icon: 'sunny-outline' as const },
|
||||
{ id: 'advanced', label: copy.experiences.advanced, icon: 'flask-outline' as const },
|
||||
]).map((opt) => (
|
||||
<TouchableOpacity key={opt.id} style={[styles.optionRow, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
|
||||
<View style={styles.optionRowLeft}>
|
||||
<Ionicons name={opt.icon} size={18} color={colors.primary} style={{ marginRight: 10 }} />
|
||||
<Text style={[styles.rowText, { color: colors.text }]}>{opt.label}</Text>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={16} color={colors.textMuted} />
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
// Schritt 5 - Attribution. Ganz ans Ende verschoben: die Frage nuetzt uns,
|
||||
// nicht dem Nutzer. Die unwahrscheinlichen Optionen (TV, Radio, "meine
|
||||
// Pflanze") brechen das Muster und machen den letzten Tap leicht.
|
||||
if (currentStep === 4) {
|
||||
return (
|
||||
<View style={styles.optionsWrap}>
|
||||
<Text style={styles.stepTitle}>{copy.stepSource}</Text>
|
||||
<View style={styles.optionsGrid}>
|
||||
{([
|
||||
{ id: 'app_store', label: copy.sources.app_store, icon: 'logo-apple' as const },
|
||||
{ id: 'instagram', label: copy.sources.instagram, icon: 'logo-instagram' as const },
|
||||
{ id: 'tiktok', label: copy.sources.tiktok, icon: 'musical-notes-outline' as const },
|
||||
{ id: 'friend', label: copy.sources.friend, icon: 'people-outline' as const },
|
||||
{ id: 'search', label: copy.sources.search, icon: 'search-outline' as const },
|
||||
{ id: 'tv', label: copy.sources.tv, icon: 'tv-outline' as const },
|
||||
{ id: 'radio', label: copy.sources.radio, icon: 'radio-outline' as const },
|
||||
{ id: 'plant', label: copy.sources.plant, icon: 'leaf-outline' as const },
|
||||
{ id: 'other', label: copy.sources.other, icon: 'sparkles-outline' as const },
|
||||
]).map((opt) => (
|
||||
<TouchableOpacity key={opt.id} style={[styles.pillBtn, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
|
||||
<Ionicons name={opt.icon} size={15} color={colors.primary} style={{ marginRight: 4 }} />
|
||||
<Text style={[styles.pillLabel, { color: colors.text }]}>{opt.label}</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
// Alle Fragen beantwortet -> Abschluss-CTA.
|
||||
if (currentStep >= TOTAL_STEPS) {
|
||||
return (
|
||||
<View style={styles.footerWrap}>
|
||||
<TouchableOpacity style={[styles.continueCta, { backgroundColor: colors.primary }]} onPress={onFinish} activeOpacity={0.86}>
|
||||
@@ -562,7 +667,7 @@ export default function OnboardingChatScreen() {
|
||||
);
|
||||
};
|
||||
|
||||
const progressPercent = `${Math.min(currentStep * 25, 100)}%`;
|
||||
const progressPercent = `${Math.min(Math.round((currentStep / TOTAL_STEPS) * 100), 100)}%`;
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
|
||||
Reference in New Issue
Block a user