141 lines
3.9 KiB
TypeScript
141 lines
3.9 KiB
TypeScript
import { useState } from "react";
|
|
import { View, Text, Pressable, SafeAreaView } from "react-native";
|
|
import * as SecureStore from "expo-secure-store";
|
|
import { router } from "expo-router";
|
|
import { Colors } from "@/constants/theme";
|
|
|
|
const STEPS = [
|
|
{
|
|
key: "homeView",
|
|
question: "When you open the app…",
|
|
options: [
|
|
{ value: "dashboard", label: "Show recent incidents", desc: "Dashboard with the last 10 events" },
|
|
{ value: "capture", label: "Go straight to capture", desc: "New incident form opens immediately" },
|
|
],
|
|
},
|
|
{
|
|
key: "inputMode",
|
|
question: "How do you prefer to input incidents?",
|
|
options: [
|
|
{ value: "form", label: "Typed form", desc: "Fill in each field manually" },
|
|
{ value: "voice", label: "Voice dictation", desc: "Speak and let the app transcribe" },
|
|
],
|
|
},
|
|
{
|
|
key: "gitea",
|
|
question: "Do you want to push incidents to Gitea?",
|
|
options: [
|
|
{ value: "later", label: "Configure later", desc: "Set it up in Settings anytime" },
|
|
{ value: "now", label: "Set up now", desc: "You'll be redirected to Settings" },
|
|
],
|
|
},
|
|
] as const;
|
|
|
|
export default function OnboardingScreen() {
|
|
const [step, setStep] = useState(0);
|
|
const [answers, setAnswers] = useState<Record<string, string>>({});
|
|
|
|
const current = STEPS[step];
|
|
|
|
async function choose(value: string) {
|
|
const next = { ...answers, [current.key]: value };
|
|
setAnswers(next);
|
|
|
|
if (step < STEPS.length - 1) {
|
|
setStep((s) => s + 1);
|
|
} else {
|
|
await SecureStore.setItemAsync("pref_home_view", next.homeView ?? "dashboard");
|
|
await SecureStore.setItemAsync("pref_input_mode", next.inputMode ?? "form");
|
|
await SecureStore.setItemAsync("onboarding_done", "true");
|
|
|
|
if (next.gitea === "now") {
|
|
router.replace("/settings");
|
|
} else {
|
|
router.replace("/");
|
|
}
|
|
}
|
|
}
|
|
|
|
const progress = ((step + 1) / STEPS.length) * 100;
|
|
|
|
return (
|
|
<SafeAreaView style={{ flex: 1, backgroundColor: Colors.bg }}>
|
|
<View style={{ flex: 1, padding: 24, justifyContent: "center" }}>
|
|
{/* Progress bar */}
|
|
<View
|
|
style={{
|
|
height: 3,
|
|
backgroundColor: Colors.border,
|
|
borderRadius: 2,
|
|
marginBottom: 48,
|
|
}}
|
|
>
|
|
<View
|
|
style={{
|
|
height: 3,
|
|
width: `${progress}%`,
|
|
backgroundColor: Colors.primary,
|
|
borderRadius: 2,
|
|
}}
|
|
/>
|
|
</View>
|
|
|
|
<Text
|
|
style={{
|
|
color: Colors.textDim,
|
|
fontSize: 12,
|
|
fontWeight: "700",
|
|
textTransform: "uppercase",
|
|
letterSpacing: 1,
|
|
marginBottom: 12,
|
|
}}
|
|
>
|
|
{step + 1} / {STEPS.length}
|
|
</Text>
|
|
|
|
<Text
|
|
style={{
|
|
color: Colors.text1,
|
|
fontSize: 26,
|
|
fontWeight: "700",
|
|
marginBottom: 32,
|
|
lineHeight: 34,
|
|
}}
|
|
>
|
|
{current.question}
|
|
</Text>
|
|
|
|
<View style={{ gap: 12 }}>
|
|
{current.options.map((opt) => (
|
|
<Pressable
|
|
key={opt.value}
|
|
onPress={() => choose(opt.value)}
|
|
style={{
|
|
backgroundColor: Colors.surface,
|
|
borderRadius: 12,
|
|
padding: 20,
|
|
borderWidth: 1,
|
|
borderColor: Colors.border,
|
|
}}
|
|
>
|
|
<Text
|
|
style={{
|
|
color: Colors.text1,
|
|
fontSize: 17,
|
|
fontWeight: "600",
|
|
marginBottom: 4,
|
|
}}
|
|
>
|
|
{opt.label}
|
|
</Text>
|
|
<Text style={{ color: Colors.text2, fontSize: 14 }}>
|
|
{opt.desc}
|
|
</Text>
|
|
</Pressable>
|
|
))}
|
|
</View>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|