wip: tracking plan

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-15 15:54:00 +00:00
parent 9d5b1e25e2
commit a2de72ab72
3 changed files with 260 additions and 5 deletions
+16 -3
View File
@@ -1,13 +1,15 @@
/**
* App.tsx root component for the DocuElevate mobile app.
*
* Wraps the entire app in the AuthProvider and renders either the login
* screen (unauthenticated) or the main tab navigator (authenticated).
* Wraps the entire app in the AuthProvider and renders either the
* authenticated tab navigator or an unauthenticated auth stack that takes
* the user through WelcomeScreen → LoginScreen.
* Push notification registration is handled by the usePushNotifications hook.
*/
import { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { Ionicons } from "@expo/vector-icons";
import React from "react";
import { ActivityIndicator, StyleSheet, Text, View } from "react-native";
@@ -18,8 +20,19 @@ import FilesScreen from "./src/screens/FilesScreen";
import LoginScreen from "./src/screens/LoginScreen";
import ProfileScreen from "./src/screens/ProfileScreen";
import UploadScreen from "./src/screens/UploadScreen";
import WelcomeScreen, { type AuthStackParamList } from "./src/screens/WelcomeScreen";
const Tab = createBottomTabNavigator();
const AuthStack = createNativeStackNavigator<AuthStackParamList>();
function AuthNavigator() {
return (
<AuthStack.Navigator screenOptions={{ headerShown: false }}>
<AuthStack.Screen name="Welcome" component={WelcomeScreen} />
<AuthStack.Screen name="Login" component={LoginScreen} />
</AuthStack.Navigator>
);
}
function TabNavigator() {
const { isAuthenticated } = useAuth();
@@ -97,7 +110,7 @@ function AppContent() {
return (
<NavigationContainer>
{isAuthenticated ? <TabNavigator /> : <LoginScreen />}
{isAuthenticated ? <TabNavigator /> : <AuthNavigator />}
</NavigationContainer>
);
}
+27 -2
View File
@@ -1,11 +1,12 @@
/**
* LoginScreen entry point for unauthenticated users.
* LoginScreen server URL entry and SSO sign-in.
*
* Renders a server URL input and a "Sign in with SSO" button that opens the
* DocuElevate web login page in the system browser. On success the
* AuthContext stores the API token and navigates to the main app.
*/
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
import React, { useState } from "react";
import {
ActivityIndicator,
@@ -20,8 +21,13 @@ import {
View,
} from "react-native";
import { useAuth } from "../context/AuthContext";
import type { AuthStackParamList } from "./WelcomeScreen";
export default function LoginScreen() {
type LoginScreenProps = {
navigation: NativeStackNavigationProp<AuthStackParamList, "Login">;
};
export default function LoginScreen({ navigation }: LoginScreenProps) {
const { signIn } = useAuth();
const [serverUrl, setServerUrl] = useState("");
const [loading, setLoading] = useState(false);
@@ -97,6 +103,15 @@ export default function LoginScreen() {
<Text style={styles.hint}>
You will be redirected to your organisation's sign-in page.
</Text>
<Pressable
onPress={() => navigation.navigate("Welcome")}
accessibilityRole="button"
accessibilityLabel="Back to welcome screen"
style={styles.backLink}
>
<Text style={styles.backLinkText}> Back</Text>
</Pressable>
</View>
</KeyboardAvoidingView>
);
@@ -179,4 +194,14 @@ const styles = StyleSheet.create({
color: "#9ca3af",
textAlign: "center",
},
backLink: {
marginTop: 20,
alignItems: "center",
minHeight: 44,
justifyContent: "center",
},
backLinkText: {
fontSize: 13,
color: "#6b7280",
},
});
+217
View File
@@ -0,0 +1,217 @@
/**
* WelcomeScreen first screen shown to unauthenticated users on first launch.
*
* Presents the DocuElevate brand, a short description of what the app does,
* and a "Get Started" button that navigates to the LoginScreen.
*/
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
import React from "react";
import {
Image,
Pressable,
SafeAreaView,
ScrollView,
StyleSheet,
Text,
View,
} from "react-native";
export type AuthStackParamList = {
Welcome: undefined;
Login: undefined;
};
type WelcomeScreenProps = {
navigation: NativeStackNavigationProp<AuthStackParamList, "Welcome">;
};
const FEATURES: { icon: string; title: string; description: string }[] = [
{
icon: "🔍",
title: "OCR & Text Extraction",
description: "Convert scanned PDFs and images into fully searchable text automatically.",
},
{
icon: "🤖",
title: "AI Metadata Extraction",
description: "AI classifies documents and pulls out key fields like dates, amounts, and subjects.",
},
{
icon: "☁️",
title: "Multi-Cloud Storage",
description: "Route processed files to Dropbox, Google Drive, OneDrive, S3, Nextcloud, and more.",
},
];
export default function WelcomeScreen({ navigation }: WelcomeScreenProps) {
return (
<SafeAreaView style={styles.safe}>
<ScrollView
contentContainerStyle={styles.scroll}
showsVerticalScrollIndicator={false}
>
{/* Hero */}
<View style={styles.hero}>
<View style={styles.logoContainer}>
<Image
source={require("../../../assets/logo.png")}
style={styles.logoImage}
resizeMode="contain"
accessibilityLabel="DocuElevate logo"
/>
</View>
<Text style={styles.appName}>DocuElevate</Text>
<Text style={styles.tagline}>Intelligent Document Processing</Text>
<Text style={styles.heroDescription}>
Ingest documents, run OCR, extract metadata with AI, and route files
to your cloud storage all in one seamless pipeline.
</Text>
</View>
{/* Feature highlights */}
<View style={styles.features}>
{FEATURES.map((feature) => (
<View key={feature.title} style={styles.featureRow}>
<Text style={styles.featureIcon} aria-hidden>{feature.icon}</Text>
<View style={styles.featureText}>
<Text style={styles.featureTitle}>{feature.title}</Text>
<Text style={styles.featureDescription}>{feature.description}</Text>
</View>
</View>
))}
</View>
{/* CTA */}
<Pressable
style={({ pressed }) => [styles.button, pressed && styles.buttonPressed]}
onPress={() => navigation.navigate("Login")}
accessibilityRole="button"
accessibilityLabel="Get started — connect to your DocuElevate server"
>
<Text style={styles.buttonText}>Get Started</Text>
</Pressable>
<Text style={styles.hint}>
Connect to your self-hosted or cloud DocuElevate server.
</Text>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safe: {
flex: 1,
backgroundColor: "#1e40af",
},
scroll: {
flexGrow: 1,
paddingHorizontal: 28,
paddingTop: 48,
paddingBottom: 40,
},
hero: {
alignItems: "center",
marginBottom: 40,
},
logoContainer: {
width: 96,
height: 96,
borderRadius: 24,
backgroundColor: "rgba(255,255,255,0.15)",
alignItems: "center",
justifyContent: "center",
marginBottom: 16,
},
logoImage: {
width: 72,
height: 72,
},
appName: {
fontSize: 36,
fontWeight: "800",
color: "#ffffff",
textAlign: "center",
marginBottom: 6,
letterSpacing: 0.5,
},
tagline: {
fontSize: 14,
fontWeight: "600",
color: "rgba(255,255,255,0.75)",
textTransform: "uppercase",
letterSpacing: 1.5,
textAlign: "center",
marginBottom: 16,
},
heroDescription: {
fontSize: 16,
color: "rgba(255,255,255,0.85)",
textAlign: "center",
lineHeight: 24,
maxWidth: 320,
},
features: {
backgroundColor: "rgba(255,255,255,0.1)",
borderRadius: 16,
paddingVertical: 8,
paddingHorizontal: 16,
marginBottom: 36,
},
featureRow: {
flexDirection: "row",
alignItems: "flex-start",
paddingVertical: 14,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "rgba(255,255,255,0.2)",
},
featureIcon: {
fontSize: 24,
marginRight: 14,
marginTop: 1,
},
featureText: {
flex: 1,
},
featureTitle: {
fontSize: 15,
fontWeight: "600",
color: "#ffffff",
marginBottom: 2,
},
featureDescription: {
fontSize: 13,
color: "rgba(255,255,255,0.75)",
lineHeight: 18,
},
button: {
backgroundColor: "#ffffff",
borderRadius: 12,
paddingVertical: 16,
alignItems: "center",
justifyContent: "center",
minHeight: 52,
shadowColor: "#000",
shadowOpacity: 0.15,
shadowOffset: { width: 0, height: 4 },
shadowRadius: 8,
elevation: 4,
marginBottom: 16,
},
buttonPressed: {
opacity: 0.9,
transform: [{ scale: 0.98 }],
},
buttonText: {
color: "#1e40af",
fontSize: 17,
fontWeight: "700",
letterSpacing: 0.3,
},
hint: {
fontSize: 12,
color: "rgba(255,255,255,0.55)",
textAlign: "center",
},
});