feat: production-ready UI with onboarding wizard and dashboard
- Onboarding flow (welcome -> Codex setup -> Claude setup -> done) with dark-themed screens and step indicators; completion stored in SecureStore - Smart root redirect checks onboarding state on launch - Dashboard tab shows both services at a glance via ServiceStatusCard; useFocusEffect + reload() keeps it fresh when credentials change in tabs - Settings tab manages credentials and exposes re-run setup wizard - Polished Codex and Claude detail screens with service headers and empty states - 4-tab layout (Home, Codex, Claude, Settings) with dark-mode tab bar - Both hooks gain reload() for cross-tab credential refresh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { View, Text, TouchableOpacity } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { router } from "expo-router";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
import { markOnboardingComplete } from "@/lib/setupState";
|
||||
import { loadCodexAuth, loadClaudeSessionKey } from "@/lib/storage";
|
||||
|
||||
export default function OnboardingDoneScreen() {
|
||||
const [codexConfigured, setCodexConfigured] = useState(false);
|
||||
const [claudeConfigured, setClaudeConfigured] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([loadCodexAuth(), loadClaudeSessionKey()]).then(
|
||||
([codex, claude]) => {
|
||||
setCodexConfigured(!!codex);
|
||||
setClaudeConfigured(!!claude);
|
||||
}
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleEnterApp = async () => {
|
||||
await markOnboardingComplete();
|
||||
router.replace("/(tabs)");
|
||||
};
|
||||
|
||||
const noneConfigured = !codexConfigured && !claudeConfigured;
|
||||
|
||||
return (
|
||||
<SafeAreaView className="flex-1 bg-neutral-950">
|
||||
<View className="flex-1 px-8 items-center justify-center">
|
||||
{/* Icon */}
|
||||
<View
|
||||
className="w-20 h-20 rounded-3xl items-center justify-center mb-8"
|
||||
style={{ backgroundColor: "#10a37f22" }}
|
||||
>
|
||||
<MaterialIcons
|
||||
name={noneConfigured ? "info-outline" : "check-circle"}
|
||||
size={40}
|
||||
color={noneConfigured ? "#a3a3a3" : "#10a37f"}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* Title */}
|
||||
<Text className="text-4xl font-bold text-white mb-3 text-center">
|
||||
{noneConfigured ? "Almost there" : "You're all set!"}
|
||||
</Text>
|
||||
<Text className="text-neutral-400 text-base text-center leading-relaxed mb-10">
|
||||
{noneConfigured
|
||||
? "No services configured yet. You can set them up anytime in Settings."
|
||||
: "Your services are connected and ready to monitor."}
|
||||
</Text>
|
||||
|
||||
{/* Summary cards */}
|
||||
<View className="w-full gap-y-3 mb-10">
|
||||
<View className="flex-row items-center gap-x-3 bg-neutral-900 rounded-2xl p-4 border border-neutral-800">
|
||||
<View
|
||||
className="w-10 h-10 rounded-xl items-center justify-center"
|
||||
style={{
|
||||
backgroundColor: codexConfigured ? "#10a37f22" : "#27272a",
|
||||
}}
|
||||
>
|
||||
<MaterialIcons
|
||||
name={codexConfigured ? "check-circle" : "radio-button-unchecked"}
|
||||
size={22}
|
||||
color={codexConfigured ? "#10a37f" : "#525252"}
|
||||
/>
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
<Text
|
||||
className="font-semibold"
|
||||
style={{ color: codexConfigured ? "white" : "#525252" }}
|
||||
>
|
||||
Codex CLI
|
||||
</Text>
|
||||
<Text className="text-neutral-600 text-sm">
|
||||
{codexConfigured ? "Connected" : "Not configured"}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="flex-row items-center gap-x-3 bg-neutral-900 rounded-2xl p-4 border border-neutral-800">
|
||||
<View
|
||||
className="w-10 h-10 rounded-xl items-center justify-center"
|
||||
style={{
|
||||
backgroundColor: claudeConfigured ? "#d9770622" : "#27272a",
|
||||
}}
|
||||
>
|
||||
<MaterialIcons
|
||||
name={claudeConfigured ? "check-circle" : "radio-button-unchecked"}
|
||||
size={22}
|
||||
color={claudeConfigured ? "#d97706" : "#525252"}
|
||||
/>
|
||||
</View>
|
||||
<View className="flex-1">
|
||||
<Text
|
||||
className="font-semibold"
|
||||
style={{ color: claudeConfigured ? "white" : "#525252" }}
|
||||
>
|
||||
Claude.ai
|
||||
</Text>
|
||||
<Text className="text-neutral-600 text-sm">
|
||||
{claudeConfigured ? "Connected" : "Not configured"}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* CTA */}
|
||||
<TouchableOpacity
|
||||
onPress={handleEnterApp}
|
||||
className="w-full rounded-2xl py-4 items-center"
|
||||
style={{ backgroundColor: "#10a37f" }}
|
||||
>
|
||||
<Text className="text-white font-bold text-base">Open App</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user